UNPKG

7.47 kBJavaScriptView Raw
1/*
2Copyright (c) 2012 Barnesandnoble.com, llc, Donavon West, and Domenic Denicola
3
4Permission is hereby granted, free of charge, to any person obtaining
5a copy of this software and associated documentation files (the
6"Software"), to deal in the Software without restriction, including
7without limitation the rights to use, copy, modify, merge, publish,
8distribute, sublicense, and/or sell copies of the Software, and to
9permit persons to whom the Software is furnished to do so, subject to
10the following conditions:
11
12The above copyright notice and this permission notice shall be
13included in all copies or substantial portions of the Software.
14
15THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
23*/
24(function (global, undefined) {
25 "use strict";
26
27 if (global.setImmediate) {
28 return;
29 }
30
31 var nextHandle = 1; // Spec says greater than zero
32 var tasksByHandle = {};
33 var currentlyRunningATask = false;
34 var doc = global.document;
35 var setImmediate;
36
37 function addFromSetImmediateArguments(args) {
38 tasksByHandle[nextHandle] = partiallyApplied.apply(undefined, args);
39 return nextHandle++;
40 }
41
42 // This function accepts the same arguments as setImmediate, but
43 // returns a function that requires no arguments.
44 function partiallyApplied(handler) {
45 var args = [].slice.call(arguments, 1);
46 return function() {
47 if (typeof handler === "function") {
48 handler.apply(undefined, args);
49 } else {
50 (new Function("" + handler))();
51 }
52 };
53 }
54
55 function runIfPresent(handle) {
56 // From the spec: "Wait until any invocations of this algorithm started before this one have completed."
57 // So if we're currently running a task, we'll need to delay this invocation.
58 if (currentlyRunningATask) {
59 // Delay by doing a setTimeout. setImmediate was tried instead, but in Firefox 7 it generated a
60 // "too much recursion" error.
61 setTimeout(partiallyApplied(runIfPresent, handle), 0);
62 } else {
63 var task = tasksByHandle[handle];
64 if (task) {
65 currentlyRunningATask = true;
66 try {
67 task();
68 } finally {
69 clearImmediate(handle);
70 currentlyRunningATask = false;
71 }
72 }
73 }
74 }
75
76 function clearImmediate(handle) {
77 delete tasksByHandle[handle];
78 }
79
80 function installNextTickImplementation() {
81 setImmediate = function() {
82 var handle = addFromSetImmediateArguments(arguments);
83 process.nextTick(partiallyApplied(runIfPresent, handle));
84 return handle;
85 };
86 }
87
88 function canUsePostMessage() {
89 // The test against `importScripts` prevents this implementation from being installed inside a web worker,
90 // where `global.postMessage` means something completely different and can't be used for this purpose.
91 if (global.postMessage && !global.importScripts) {
92 var postMessageIsAsynchronous = true;
93 var oldOnMessage = global.onmessage;
94 global.onmessage = function() {
95 postMessageIsAsynchronous = false;
96 };
97 global.postMessage("", "*");
98 global.onmessage = oldOnMessage;
99 return postMessageIsAsynchronous;
100 }
101 }
102
103 function installPostMessageImplementation() {
104 // Installs an event handler on `global` for the `message` event: see
105 // * https://developer.mozilla.org/en/DOM/window.postMessage
106 // * http://www.whatwg.org/specs/web-apps/current-work/multipage/comms.html#crossDocumentMessages
107
108 var messagePrefix = "setImmediate$" + Math.random() + "$";
109 var onGlobalMessage = function(event) {
110 if (event.source === global &&
111 typeof event.data === "string" &&
112 event.data.indexOf(messagePrefix) === 0) {
113 runIfPresent(+event.data.slice(messagePrefix.length));
114 }
115 };
116
117 if (global.addEventListener) {
118 global.addEventListener("message", onGlobalMessage, false);
119 } else {
120 global.attachEvent("onmessage", onGlobalMessage);
121 }
122
123 setImmediate = function() {
124 var handle = addFromSetImmediateArguments(arguments);
125 global.postMessage(messagePrefix + handle, "*");
126 return handle;
127 };
128 }
129
130 function installMessageChannelImplementation() {
131 var channel = new MessageChannel();
132 channel.port1.onmessage = function(event) {
133 var handle = event.data;
134 runIfPresent(handle);
135 };
136
137 setImmediate = function() {
138 var handle = addFromSetImmediateArguments(arguments);
139 channel.port2.postMessage(handle);
140 return handle;
141 };
142 }
143
144 function installReadyStateChangeImplementation() {
145 var html = doc.documentElement;
146 setImmediate = function() {
147 var handle = addFromSetImmediateArguments(arguments);
148 // Create a <script> element; its readystatechange event will be fired asynchronously once it is inserted
149 // into the document. Do so, thus queuing up the task. Remember to clean up once it's been called.
150 var script = doc.createElement("script");
151 script.onreadystatechange = function () {
152 runIfPresent(handle);
153 script.onreadystatechange = null;
154 html.removeChild(script);
155 script = null;
156 };
157 html.appendChild(script);
158 return handle;
159 };
160 }
161
162 function installSetTimeoutImplementation() {
163 setImmediate = function() {
164 var handle = addFromSetImmediateArguments(arguments);
165 setTimeout(partiallyApplied(runIfPresent, handle), 0);
166 return handle;
167 };
168 }
169
170 // If supported, we should attach to the prototype of global, since that is where setTimeout et al. live.
171 var attachTo = Object.getPrototypeOf && Object.getPrototypeOf(global);
172 attachTo = attachTo && attachTo.setTimeout ? attachTo : global;
173
174 // Don't get fooled by e.g. browserify environments.
175 if ({}.toString.call(global.process) === "[object process]") {
176 // For Node.js before 0.9
177 installNextTickImplementation();
178
179 } else if (canUsePostMessage()) {
180 // For non-IE10 modern browsers
181 installPostMessageImplementation();
182
183 } else if (global.MessageChannel) {
184 // For web workers, where supported
185 installMessageChannelImplementation();
186
187 } else if (doc && "onreadystatechange" in doc.createElement("script")) {
188 // For IE 6–8
189 installReadyStateChangeImplementation();
190
191 } else {
192 // For older browsers
193 installSetTimeoutImplementation();
194 }
195
196 attachTo.setImmediate = setImmediate;
197 attachTo.clearImmediate = clearImmediate;
198}(self));