UNPKG

todomvc

Version:

> Helping you select an MV\* framework

221 lines (190 loc) 6.42 kB
/** * setImmediate polyfill / shim * * (c) copyright 2011-2013 Brian Cavalier and John Hann * * poly is part of the cujo.js family of libraries (http://cujojs.com/) * * Based on NobleJS's setImmediate. (https://github.com/NobleJS/setImmediate) * * Licensed under the MIT License at: * http://www.opensource.org/licenses/mit-license.php * */ (function (global) { define(['./lib/_base'], function (base) { var testCache, tasks; testCache = {}; tasks = (function () { var nextHandle, tasksByHandle, currentlyRunningATask; nextHandle = 1; // Spec says greater than zero tasksByHandle = {}; currentlyRunningATask = false; function Task (handler, args) { this.handler = handler; this.args = Array.prototype.slice.call(args); } Task.prototype.run = function () { // See steps in section 5 of the spec. if (base.isFunction(this.handler)) { // Choice of `thisArg` is not in the setImmediate spec; `undefined` is in the setTimeout spec though: // http://www.whatwg.org/specs/web-apps/current-work/multipage/timers.html this.handler.apply(undefined, this.args); } else { var scriptSource = '' + this.handler; eval(scriptSource); } }; return { addFromSetImmediateArguments: function (args) { var handler, argsToHandle, task, thisHandle; handler = args[0]; argsToHandle = Array.prototype.slice.call(args, 1); task = new Task(handler, argsToHandle); thisHandle = nextHandle++; tasksByHandle[thisHandle] = task; return thisHandle; }, runIfPresent: function (handle) { // From the spec: "Wait until any invocations of this algorithm started before this one have completed." // So if we're currently running a task, we'll need to delay this invocation. if (!currentlyRunningATask) { var task = tasksByHandle[handle]; if (task) { currentlyRunningATask = true; try { task.run(); } finally { delete tasksByHandle[handle]; currentlyRunningATask = false; } } } else { // Delay by doing a setTimeout. setImmediate was tried instead, but in Firefox 7 it generated a // "too much recursion" error. global.setTimeout(function () { tasks.runIfPresent(handle); }, 0); } }, remove: function (handle) { delete tasksByHandle[handle]; } }; }()); function has (name) { if (base.isFunction(testCache[name])) { testCache[name] = testCache[name](global); } return testCache[name]; } function add (name, test, now) { testCache[name] = now ? test(global, d, el) : test; } function aliasMicrosoftImplementation (attachTo) { attachTo.setImmediate = global.msSetImmediate; attachTo.clearImmediate = global.msClearImmediate; } function installPostMessageImplementation (attachTo) { // Installs an event handler on `global` for the `message` event: see // * https://developer.mozilla.org/en/DOM/window.postMessage // * http://www.whatwg.org/specs/web-apps/current-work/multipage/comms.html#crossDocumentMessages var MESSAGE_PREFIX = 'cujojs/poly.setImmediate' + Math.random(); function isStringAndStartsWith (string, putativeStart) { return typeof string === 'string' && string.substring(0, putativeStart.length) === putativeStart; } function onGlobalMessage (event) { // This will catch all incoming messages (even from other windows!), so we need to try reasonably hard to // avoid letting anyone else trick us into firing off. We test the origin is still this window, and that a // (randomly generated) unpredictable identifying prefix is present. if (event.source === global && isStringAndStartsWith(event.data, MESSAGE_PREFIX)) { var handle = event.data.substring(MESSAGE_PREFIX.length); tasks.runIfPresent(handle); } } global.addEventListener('message', onGlobalMessage, false); attachTo.setImmediate = function () { var handle = tasks.addFromSetImmediateArguments(arguments); // Make `global` post a message to itself with the handle and identifying prefix, thus asynchronously // invoking our onGlobalMessage listener above. global.postMessage(MESSAGE_PREFIX + handle, '*'); return handle; }; } function installReadyStateChangeImplementation(attachTo) { attachTo.setImmediate = function () { var handle = tasks.addFromSetImmediateArguments(arguments); // Create a <script> element; its readystatechange event will be fired asynchronously once it is inserted // into the document. Do so, thus queuing up the task. Remember to clean up once it's been called. var scriptEl = global.document.createElement('script'); scriptEl.onreadystatechange = function () { tasks.runIfPresent(handle); scriptEl.onreadystatechange = null; scriptEl.parentNode.removeChild(scriptEl); scriptEl = null; }; global.document.documentElement.appendChild(scriptEl); return handle; }; } function installSetTimeoutImplementation(attachTo) { attachTo.setImmediate = function () { var handle = tasks.addFromSetImmediateArguments(arguments); global.setTimeout(function () { tasks.runIfPresent(handle); }, 0); return handle; }; } add('setimmediate', function (g) { return base.isFunction(g.setImmediate); }); add('ms-setimmediate', function (g) { return base.isFunction(g.msSetImmediate); }); add('post-message', function (g) { // Note: this is only for the async postMessage, not the buggy sync // version in IE8 var postMessageIsAsynchronous, oldOnMessage; postMessageIsAsynchronous = true; oldOnMessage = g.onmessage; if (!g.postMessage) { return false; } g.onmessage = function () { postMessageIsAsynchronous = false; }; g.postMessage('', '*'); g.onmessage = oldOnMessage; return postMessageIsAsynchronous; }); add('script-onreadystatechange', function (g) { return 'document' in g && 'onreadystatechange' in g.document.createElement('script'); }); if (!has('setimmediate')) { if (has('ms-setimmediate')) { aliasMicrosoftImplementation(global); } else { if (has('post-message')) { installPostMessageImplementation(global); } else if (has('script-onreadystatechange')) { installReadyStateChangeImplementation(global); } else { installSetTimeoutImplementation(global); } global.clearImmediate = tasks.remove; } } }); }(this.global || this));