Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed callback to always be run after the css is loaded. #31

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
356 changes: 218 additions & 138 deletions assets/js/adapt.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,141 +12,221 @@
*/

// Closure.
(function(w, d, config, undefined) {
// If no config, exit.
if (!config) {
return;
}

// Empty vars to use later.
var url, url_old, timer;

// Alias config values.
var callback = config.callback || function(){};
var path = config.path ? config.path : '';
var range = config.range;
var range_len = range.length;

// Create empty link tag:
// <link rel="stylesheet" />
var css = d.createElement('link');
css.rel = 'stylesheet';
css.media = 'screen';

// Called from within adapt().
function change(i, width) {
// Set the URL.
css.href = url;
url_old = url;

// Fire callback.
callback(i, width);
}

// Adapt to width.
function adapt() {
// This clearTimeout is for IE.
// Really it belongs in react(),
// but doesn't do any harm here.
clearTimeout(timer);

// Parse viewport width.
var width = d.documentElement ? d.documentElement.clientWidth : 0;

// While loop vars.
var arr, arr_0, val_1, val_2, is_range, file;

// How many ranges?
var i = range_len;
var last = range_len - 1;

// Start with blank URL.
url = undefined;

while (i--) {
// Turn string into array.
arr = range[i].split('=');

// Width is to the left of "=".
arr_0 = arr[0];

// File name is to the right of "=".
// Presuppoes a file with no spaces.
// If no file specified, make empty.
file = arr[1] ? arr[1].replace(/\s/g, '') : undefined;

// Assume max if "to" isn't present.
is_range = arr_0.match('to');

// If it's a range, split left/right sides of "to",
// and then convert each one into numerical values.
// If it's not a range, turn maximum into a number.
val_1 = is_range ? parseInt(arr_0.split('to')[0], 10) : parseInt(arr_0, 10);
val_2 = is_range ? parseInt(arr_0.split('to')[1], 10) : undefined;

// Check for maxiumum or range.
if ((!val_2 && i === last && width > val_1) || (width > val_1 && width <= val_2)) {
// Build full URL to CSS file.
file && (url = path + file);

// Exit the while loop. No need to continue
// if we've already found a matching range.
break;
}
}

// Was it created yet?
if (!url_old) {
// Apply changes.
change(i, width);

// If URL has been defined, add the CSS.
// Use faster document.head if possible.
url && (d.head || d.getElementsByTagName('head')[0]).appendChild(css);
}
else if (url_old !== url) {
// Apply changes.
change(i, width);
}
}

// Fire off once.
adapt();

// Slight delay.
function react() {
// Clear the timer as window resize fires,
// so that it only calls adapt() when the
// user has finished resizing the window.
clearTimeout(timer);

// Start the timer countdown.
timer = setTimeout(adapt, 16);
// -----------------------^^
// Note: 15.6 milliseconds is lowest "safe"
// duration for setTimeout and setInterval.
//
// http://www.nczonline.net/blog/2011/12/14/timer-resolution-in-browsers
}

// Do we want to watch for
// resize and device tilt?
if (config.dynamic) {
// Event listener for window resize,
// also triggered by phone rotation.
if (w.addEventListener) {
// Good browsers.
w.addEventListener('resize', react, false);
}
else if (w.attachEvent) {
// Legacy IE support.
w.attachEvent('onresize', react);
}
else {
// Old-school fallback.
w.onresize = react;
}
}

// Pass in window, document, config, undefined.
})(this, this.document, ADAPT_CONFIG);
(function (w, d, config, undefined) {
// If no config, exit.
if (!config) {
return;
}

// Empty vars to use later.
var docHead, docBody, bodyClass, url, url_old, timer, css, css_old;

// Alias config values.
var callback = config.callback || function () { },
path = config.path ? config.path : '',
range = config.range,
range_len = range.length;

function setupAdapt() {
d.removeEventListener( "DOMContentLoaded", setupAdapt );

// Cache <head> and <body> element reference
// Use faster document.head and document.body if possible.
docHead = (d.head || d.getElementsByTagName('head')[0]);
docBody = (d.body || d.getElementsByTagName('body')[0]);
bodyClass = '';

// Get existing link tag if cssId was provided
if (config.cssId)
css = d.getElementById(config.cssId);

// Fire off once.
adapt();

// Clean up any preexisting body classes
var classesToRemove = [];
for (var i = 0; i < docBody.classList.length; i++) {
if( docBody.classList[i].charAt(0) == '_' && docBody.classList[i] != bodyClass )
classesToRemove[classesToRemove.length] = docBody.classList[i];
}
for (var i = 0; i < classesToRemove.length; i++) {
docBody.classList.remove(classesToRemove[i]);
}

// Do we want to watch for
// resize and device tilt?
if (config.dynamic) {
// Event listener for window resize,
// also triggered by phone rotation.
if (w.addEventListener) {
// Good browsers.
w.addEventListener('resize', react, false);
}
else if (w.attachEvent) {
// Legacy IE support.
w.attachEvent('onresize', react);
}
else {
// Old-school fallback.
w.onresize = react;
}
}
}

// Called from within adapt().
function change(i, width, linkElement) {
// Set the URL.
linkElement.href = url;
url_old = url;

linkElement.onload = function () {
// Remove old css
if(css_old) {
docHead.removeChild(css_old);
css_old = undefined;
}
// Fire callback.
callback(i, width);
};
}

// Adapt to width.
function adapt() {
// This clearTimeout is for IE.
// Really it belongs in react(),
// but doesn't do any harm here.
clearTimeout(timer);

// Remove old css
if(css_old) {
docHead.removeChild(css_old);
css_old = undefined;
}

// Parse viewport width.
var width = d.documentElement ? d.documentElement.clientWidth : 0;

// While loop vars.
var arr, arr_0, val_1, val_2, is_range, file;

// How many ranges?
var i = range_len;
var last = range_len - 1;

// Start with blank URL.
url = undefined;

while (i--) {

// Find first occurance of "=".
equalPos = range[i].indexOf('=');

// Width is to the left of "=".
arr_0 = range[i].substr(0, equalPos);

// File name is to the right of "=".
// Presuppoes a file with no spaces.
// If no file specified, make empty.
file = range[i].substr(equalPos+1);
file = file ? file.replace(/\s/g, '') : undefined;

// Assume max if "to" isn't present.
is_range = arr_0.match('to');

// If it's a range, split left/right sides of "to",
// and then convert each one into numerical values.
// If it's not a range, turn maximum into a number.
val_1 = is_range ? parseInt(arr_0.split('to')[0], 10) : parseInt(arr_0, 10);
val_2 = is_range ? parseInt(arr_0.split('to')[1], 10) : undefined;

// Check for maxiumum or range.
if ((!val_2 && i === last && width > val_1) || (width > val_1 && width <= val_2)) {
// Build full URL to CSS file.
file && (url = path + file);

// Exit the while loop. No need to continue
// if we've already found a matching range.
break;
}
}

if (url) {
// Update css link tag, only if css url has changed
if(!url_old || url_old !== url) {
if (css) {
if(css.href !== url) {
// Create empty link tag:
// <link rel="stylesheet" />
var newCss = d.createElement('link');
newCss.rel = 'stylesheet';
newCss.media = css.media;
newCss.id = css.id;
// Apply changes.
change(i, width, newCss);

// Prevent overflowing flash of content
css_old = css;
css_old.id += '_old'; //Prevent duplicate IDs

// Preserve css load order with graceful content stepping
if(css.nextSibling)
docHead.insertBefore(newCss, css.nextSibling);
else
docHead.appendChild(newCss);
css = newCss;
} else {
// Fire callback even if css link element wasn't changed.
url_old = url;
callback(i, width);
}
} else {
// Create empty link tag:
// <link rel="stylesheet" />
css = d.createElement('link');
css.rel = 'stylesheet';
css.media = 'screen';
// Apply changes.
change(i, width, css);

// Append css to end of head
docHead.appendChild(css);
}
}

// Update css body class if it has changed
var newBodyClass = '_' + url.split('/').pop().split('.')[0];
if( newBodyClass != bodyClass ) {
// Remove bodyClass
if (bodyClass.length && docBody.classList.contains(bodyClass))
docBody.classList.remove(bodyClass);
bodyClass = newBodyClass;
// Add bodyClass
docBody.classList.add(bodyClass);
}
}
}

// Slight delay.
function react() {
// Clear the timer as window resize fires,
// so that it only calls adapt() when the
// user has finished resizing the window.
clearTimeout(timer);

// Start the timer countdown.
timer = setTimeout(adapt, 20);
// -----------------------^^
// Note: 20 milliseconds is lowest "safe"
// duration for setTimeout and setInterval.
}

// Setup adapt after DOM is loaded
if ( d.readyState === "complete" ||
( d.readyState !== "loading" && !d.documentElement.doScroll ) ) {
setupAdapt();
} else {
d.addEventListener( "DOMContentLoaded", setupAdapt );
}

// Pass in window, document, config, undefined.
})(this, this.document, ADAPT_CONFIG);
10 changes: 9 additions & 1 deletion assets/js/adapt.min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.