diff --git a/src/curl.js b/src/curl.js index 96fc708b..a2c5b6ea 100644 --- a/src/curl.js +++ b/src/curl.js @@ -9,29 +9,30 @@ * Licensed under the MIT License at: * http://www.opensource.org/licenses/mit-license.php * - * @version 0.6.2 */ (function (global) { var - version = '0.6.2', + version = '0.6.2a', userCfg = global['curl'], doc = global.document, head = doc && (doc['head'] || doc.getElementsByTagName('head')[0]), -// // constants / flags + // to keep IE from crying, we need to put scripts before any + // elements, but after any . this should do it: + insertBeforeEl = head && head.getElementsByTagName('base')[0] || null, + // constants / flags msgUsingExports = {}, msgFactoryExecuted = {}, - interactive = {}, // this is the list of scripts that IE is loading. one of these will // be the "interactive" script. too bad IE doesn't send a readystatechange // event to tell us exactly which one. activeScripts = {}, + // readyStates for IE6-9 + readyStates = 'addEventListener' in global ? {} : { 'loaded': 1, 'complete': 1 }, // these are always handy :) cleanPrototype = {}, toString = cleanPrototype.toString, undef, - // script ready states that signify it's loaded - readyStates = { 'loaded': 1, 'interactive': interactive, 'complete': 1 }, // local cache of resource definitions (lightweight promises) cache = {}, // preload are files that must be loaded before any others @@ -230,7 +231,7 @@ function toUrl (n) { // even though internally, we don't seem to need to do // toAbsId, the AMD spec says we need to do this for plugins. - // also, thesec states that we should not append an extension + // also, the spec states that we should not append an extension // in this function. return core.resolvePathInfo(toAbsId(n), cfg).url; } @@ -517,6 +518,9 @@ function process (ev) { ev = ev || global.event; // detect when it's done loading + // ev.type == 'load' is for all browsers except IE6-9 + // IE6-9 need to use onreadystatechange and look for + // el.readyState in {loaded, complete} (yes, we need both) if (ev.type == 'load' || readyStates[el.readyState]) { delete activeScripts[def.id]; // release event listeners @@ -547,9 +551,8 @@ // IE will load the script sync if it's in the cache, so // indicate the current resource definition if this happens. activeScripts[def.id] = el; - // use insertBefore to keep IE from throwing Operation Aborted (thx Bryan Forbes!) - head.insertBefore(el, head.firstChild); + head.insertBefore(el, insertBeforeEl); }, extractCjsDeps: function (defFunc) { @@ -569,7 +572,7 @@ else if (!currQuote) { ids.push(id); } - return m; // uses least RAM/CPU + return ''; // uses least RAM/CPU }); return ids; }, @@ -683,7 +686,7 @@ // signal any waiters/parents that we can export // early (see progress callback in getDep below). // note: this may fire for `require` as well, if it - // is listed after `module` or `exports` in teh deps list, + // is listed after `module` or `exports` in the deps list, // but that is okay since all waiters will only record // it once. if (parentDef.exports) { @@ -906,7 +909,7 @@ }, getCurrentDefName: function () { - // IE marks the currently executing thread as "interactive" + // IE6-9 mark the currently executing thread as "interactive" // Note: Opera lies about which scripts are "interactive", so we // just have to test for it. Opera provides a true browser test, not // a UA sniff, thankfully. @@ -914,7 +917,7 @@ var def; if (!isType(global.opera, 'Opera')) { for (var d in activeScripts) { - if (readyStates[activeScripts[d].readyState] == interactive) { + if (activeScripts[d].readyState == 'interactive') { def = d; break; } diff --git a/src/curl/plugin/js.js b/src/curl/plugin/js.js index 2846c99d..21ef63d8 100644 --- a/src/curl/plugin/js.js +++ b/src/curl/plugin/js.js @@ -28,14 +28,12 @@ * http://wiki.whatwg.org/wiki/Dynamic_Script_Execution_Order * */ -(function (global, doc) { +(function (global, doc, testGlobalVar) { +define(/*=='js',==*/ ['curl/_privileged'], function (priv) { "use strict"; var cache = {}, queue = [], supportsAsyncFalse = doc && doc.createElement('script').async == true, - readyStates = { 'loaded': 1, 'interactive': 1, 'complete': 1 }, - orsc = 'onreadystatechange', - head = doc && (doc['head'] || doc.getElementsByTagName('head')[0]), waitForOrderedScript, undef; @@ -44,43 +42,31 @@ name + '.' + defaultExt : name; } - // TODO: find a way to reuse the loadScript from curl.js function loadScript (def, success, failure) { // script processing rules learned from RequireJS - var deadline, el; + var deadline, completed, el; // default deadline is very far in the future (5 min) // devs should set something reasonable if they want to use it - deadline = new Date().valueOf() + (def.timeoutMsec || 300) * 1000; - - // insert script - el = doc.createElement('script'); + deadline = new Date().valueOf() + (def.timeoutMsec || 300000); // initial script processing - function process (ev) { - ev = ev || global.event; - // detect when it's done loading - if (ev.type == 'load' || readyStates[el.readyState]) { - // release event listeners - el.onload = el[orsc] = el.onerror = ""; - if (def.exports) def.resolved = testGlobalVar(def.exports); - if (!def.exports || def.resolved) { - success(el); - } - else { - fail(); - } + function process () { + completed = true; + if (def.exports) def.resolved = testGlobalVar(def.exports); + if (!def.exports || def.resolved) { + success(el); // pass el so it can be removed (text/cache) + } + else { + failure(); } } - function fail () { - // some browsers send an event, others send a string, - // but none of them send anything useful, so just say we failed: - el.onload = el[orsc] = el.onerror = ""; - if (failure) { - failure(new Error('Script error or http error: ' + def.url)); - } + function fail (ex) { + // Exception is squashed by curl.js unfortunately + completed = true; + failure(ex); } // some browsers (Opera and IE6-8) don't support onerror and don't fire @@ -89,31 +75,19 @@ // is defined (see below) function poller () { // if the script loaded - if (el.onload && readyStates[el.readyState]) { - process({}); - } - // if neither process or fail as run and our deadline is in the past - else if (el.onload && deadline < new Date()) { - fail(); - } - else { - setTimeout(poller, 10); + if (!completed) { + // if neither process or fail as run and our deadline is in the past + if (deadline < new Date()) { + failure(); + } + else { + setTimeout(poller, 10); + } } } if (failure && def.exports) setTimeout(poller, 10); - // set type first since setting other properties could - // prevent us from setting this later - el.type = def.mimetype || 'text/javascript'; - // using dom0 event handlers instead of wordy w3c/ms - el.onload = el[orsc] = process; - el.onerror = fail; - el.charset = def.charset || 'utf-8'; - el.async = !def.order; - el.src = def.url; - - // use insertBefore to keep IE from throwing Operation Aborted (thx Bryan Forbes!) - head.insertBefore(el, head.firstChild); + el = priv['core'].loadScript(def, process, fail); } @@ -137,16 +111,7 @@ } - function testGlobalVar (varName) { - try { - return eval('global.' + varName); - } - catch (ex) { - return undef; - } - } - - define(/*=='js',==*/ { + return { // the !options force us to cache ids in the plugin 'dynamic': true, @@ -194,8 +159,8 @@ def.mimetype = 'text/cache'; loadScript(def, // remove the fake script when loaded - function (el) { el.parentNode.removeChild(el); }, - false + function (el) { el && el.parentNode.removeChild(el); }, + function () {} ); def.mimetype = ''; } @@ -209,6 +174,10 @@ } - }); - -}(this, this.document)); + }; +}); +}( + this, + this.document, + function () { try { return eval(arguments[0]); } catch (ex) { return; } } +));