/** MIT License (c) copyright B Cavalier & J Hann */ /** * curl domReady * * Licensed under the MIT License at: * http://www.opensource.org/licenses/mit-license.php */ /** * usage: * require(['ModuleA', 'curl/domReady'], function (ModuleA, domReady) { * var a = new ModuleA(); * domReady(function () { * document.body.appendChild(a.domNode); * }); * }); * * also: check out curl's domReady! plugin * * HT to Bryan Forbes who wrote the initial domReady code: * http://www.reigndropsfall.net/ * */ (function (global, doc) { var readyState = 'readyState', // keep these quoted so closure compiler doesn't squash them readyStates = { 'loaded': 1, 'interactive': 1, 'complete': 1 }, callbacks = [], fixReadyState = doc && typeof doc[readyState] != "string", // IE needs this cuz it won't stop setTimeout if it's already queued up completed = false, pollerTime = 10, addEvent, remover, removers = [], pollerHandle, undef; function ready () { completed = true; clearTimeout(pollerHandle); while (remover = removers.pop()) remover(); if (fixReadyState) { doc[readyState] = "complete"; } // callback all queued callbacks var cb; while ((cb = callbacks.shift())) { cb(); } } var testEl; function isDomManipulable () { // question: implement Diego Perini's IEContentLoaded instead? // answer: The current impl seems more future-proof rather than a // non-standard method (doScroll). i don't care if the rest of the js // world is using doScroll! They can have fun repairing their libs when // the IE team removes doScroll in IE 13. :) if (!doc.body) return false; // no body? we're definitely not ready! if (!testEl) testEl = doc.createTextNode(''); try { // webkit needs to use body. doc doc.body.removeChild(doc.body.appendChild(testEl)); testEl = undef; return true; } catch (ex) { return false; } } function checkDOMReady (e) { var isReady; // all browsers except IE will be ready when readyState == 'interactive' // so we also must check for document.body isReady = readyStates[doc[readyState]] && isDomManipulable(); if (!completed && isReady) { ready(); } return isReady; } function poller () { checkDOMReady(); if (!completed) { pollerHandle = setTimeout(poller, pollerTime); } } // select the correct event listener function. all of our supported // browsers will use one of these if ('addEventListener' in global) { addEvent = function (node, event) { node.addEventListener(event, checkDOMReady, false); return function () { node.removeEventListener(event, checkDOMReady, false); }; }; } else { addEvent = function (node, event) { node.attachEvent('on' + event, checkDOMReady); return function () { node.detachEvent(event, checkDOMReady); }; }; } if (doc) { if (!checkDOMReady()) { // add event listeners and collect remover functions removers = [ addEvent(global, 'load'), addEvent(doc, 'readystatechange'), addEvent(global, 'DOMContentLoaded') ]; // additionally, poll for readystate pollerHandle = setTimeout(poller, pollerTime); } } define(/*=='curl/domReady',==*/ function () { // this is simply a callback, but make it look like a promise function domReady (cb) { if (completed) cb(); else callbacks.push(cb); } domReady['then'] = domReady; domReady['amd'] = true; return domReady; }); }(this, this.document));