modulex-util
Version:
common utilities from modulex
243 lines (224 loc) • 6.94 kB
JavaScript
/**
* this code can only run at browser environment
* @ignore
* @author lifesinger@gmail.com, yiminghe@gmail.com
*/
var util = require('./base');
var win = typeof window !== 'undefined' ? window : {},
doc = win.document || {},
docElem = doc.documentElement,
EMPTY = '',
domReady = 0,
callbacks = [],
// The number of poll times.
POLL_RETIRES = 500,
// The poll interval in milliseconds.
POLL_INTERVAL = 40,
// #id or id
RE_ID_STR = /^#?([\w-]+)$/,
RE_NOT_WHITESPACE = /\S/,
standardEventModel = doc.addEventListener,
supportEvent = doc.attachEvent || standardEventModel,
DOM_READY_EVENT = 'DOMContentLoaded',
READY_STATE_CHANGE_EVENT = 'readystatechange',
LOAD_EVENT = 'load',
COMPLETE = 'complete',
/*global addEventListener:true, removeEventListener:true*/
addEventListener = standardEventModel ? function (el, type, fn) {
el.addEventListener(type, fn, false);
} : function (el, type, fn) {
el.attachEvent('on' + type, fn);
},
removeEventListener = standardEventModel ? function (el, type, fn) {
el.removeEventListener(type, fn, false);
} : function (el, type, fn) {
el.detachEvent('on' + type, fn);
};
util.mix(util, {
/**
* A crude way of determining if an object is a window
* @member util
*/
isWindow: function (obj) {
// must use == for ie8
/*jshint eqeqeq:false*/
return obj != null && obj == obj.window;
},
/**
* get xml representation of data
* @param {String} data
* @member util
*/
parseXml: function (data) {
// already a xml
if (data.documentElement) {
return data;
}
var xml;
// Standard
if (win.DOMParser) {
xml = new DOMParser().parseFromString(data, 'text/xml');
} else { // IE
/*global ActiveXObject*/
xml = new ActiveXObject('Microsoft.XMLDOM');
xml.async = false;
xml.loadXML(data);
}
if (!xml || !xml.documentElement || xml.getElementsByTagName('parsererror').length) {
throw new Error('Invalid XML: ' + data);
}
return xml;
},
/**
* Evaluates a script in a global context.
* @member util
*/
globalEval: function (data) {
if (data && RE_NOT_WHITESPACE.test(data)) {
// http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context
// http://msdn.microsoft.com/en-us/library/ie/ms536420(v=vs.85).aspx always return null
/*jshint evil:true*/
if (win.execScript) {
win.execScript(data);
} else {
(function (data) {
win['eval'].call(win, data);
})(data);
}
}
},
/**
* Specify a function to execute when the Dom is fully loaded.
* @param fn {Function} A function to execute after the Dom is ready
* @chainable
* @member util
*/
ready: function (fn) {
if (domReady) {
if ('@DEBUG@') {
fn();
} else {
try {
fn();
} catch (e) {
setTimeout(function () {
throw e;
}, 0);
}
}
} else {
callbacks.push(fn);
}
return this;
},
/**
* Executes the supplied callback when the item with the supplied id is found.
* @param id {String} The id of the element, or an array of ids to look for.
* @param fn {Function} What to execute when the element is found.
* @member util
*/
available: function (id, fn) {
id = (id + EMPTY).match(RE_ID_STR)[1];
var retryCount = 1;
var timer = util.later(function () {
if (++retryCount > POLL_RETIRES) {
timer.cancel();
return;
}
var node = doc.getElementById(id);
if (node) {
fn(node);
timer.cancel();
}
}, POLL_INTERVAL, true);
}
});
util.parseXML = util.parseXml;
function fireReady() {
if (domReady) {
return;
}
// nodejs
if (win && win.setTimeout) {
removeEventListener(win, LOAD_EVENT, fireReady);
}
domReady = 1;
for (var i = 0; i < callbacks.length; i++) {
if ('@DEBUG@') {
callbacks[i]();
} else {
try {
callbacks[i]();
} catch (e) {
/*jshint loopfunc:true*/
setTimeout(function () {
throw e;
}, 0);
}
}
}
}
// Binds ready events.
function bindReady() {
// Catch cases where ready() is called after the
// browser event has already occurred.
if (!doc || doc.readyState === COMPLETE) {
fireReady();
return;
}
// A fallback to window.onload, that will always work
addEventListener(win, LOAD_EVENT, fireReady);
// w3c mode
if (standardEventModel) {
var domReady = function () {
removeEventListener(doc, DOM_READY_EVENT, domReady);
fireReady();
};
addEventListener(doc, DOM_READY_EVENT, domReady);
} else {
var stateChange = function () {
if (doc.readyState === COMPLETE) {
removeEventListener(doc, READY_STATE_CHANGE_EVENT, stateChange);
fireReady();
}
};
// ensure firing before onload (but completed after all inner iframes is loaded)
// maybe late but safe also for iframes
addEventListener(doc, READY_STATE_CHANGE_EVENT, stateChange);
// If IE and not a frame
// continually check to see if the document is ready
var notframe,
doScroll = docElem && docElem.doScroll;
try {
notframe = (win.frameElement === null);
} catch (e) {
notframe = false;
}
// can not use in iframe,parent window is dom ready so doScroll is ready too
if (doScroll && notframe) {
var readyScroll = function () {
try {
// Ref: http://javascript.nwbox.com/IEContentLoaded/
doScroll('left');
fireReady();
} catch (ex) {
setTimeout(readyScroll, POLL_INTERVAL);
}
};
readyScroll();
}
}
}
// bind on start
// in case when you bind but the DOMContentLoaded has triggered
// then you has to wait onload
// worst case no callback at all
if (supportEvent) {
bindReady();
}
try {
if (doc.execCommand) {
doc.execCommand('BackgroundImageCache', false, true);
}
} catch (e) {
}