forked from olark/lightningjs
-
Notifications
You must be signed in to change notification settings - Fork 0
/
lightningjs-embed.js
206 lines (175 loc) · 10.6 KB
/
lightningjs-embed.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
window.lightningjs || (function(modules){
var lightningjsName = 'lightningjs';
function require(moduleName, url) {
// attach the lightningjs version to the URL to make versioning possible
var lightningjsVersion = '1';
if (url) url += (/\?/.test(url) ? '&': '?') + 'lv=' + lightningjsVersion;
// declare the namespace
modules[moduleName] || (function() {
var theWindow = window,
theDocument = document,
namespace = moduleName,
protocol = theDocument.location.protocol,
load = "load",
responseCounter = 0;
(function() {
// create a callback named after the namespace, and have it
// recursively return deferred responses
modules[namespace] = function() {
var theArguments = arguments,
context = this,
// freeze in the ID of the response of this function so that
// the nested methods can depened on its response
// (used to deserialize the callstack with proper dependency
// ordering later on)
promiseResponseId = ++responseCounter,
promiseFunctionId = (context && context != theWindow) ? (context.id || 0) : 0;
// push this call onto the callstack
(internalModule.s = internalModule.s || []).push([promiseResponseId, promiseFunctionId, theArguments]);
// create a deferred function that recursively applies this
// deferred call mechanism to allow nested deferred methods
function promiseFunction() {
promiseFunction.id = promiseResponseId;
return modules[namespace].apply(promiseFunction, arguments)
}
// add then() method to implement the CommonJS Promise API
// http://wiki.commonjs.org/wiki/Promises/A
promiseFunction.then = function(fulfillmentHandler, errorHandler, progressHandler) {
// initialize the handler queues
var fulfillmentHandlers = internalModule.fh[promiseResponseId] = internalModule.fh[promiseResponseId] || [],
errorHandlers = internalModule.eh[promiseResponseId] = internalModule.eh[promiseResponseId] || [],
progressHandlers = internalModule.ph[promiseResponseId] = internalModule.ph[promiseResponseId] || [];
// enqueue the appropriate handlers
fulfillmentHandler && fulfillmentHandlers.push(fulfillmentHandler);
errorHandler && errorHandlers.push(errorHandler);
progressHandler && progressHandlers.push(progressHandler);
// return the function itself to allow chaining
return promiseFunction;
}
return promiseFunction;
};
// the internal module keeps track of all our internal state
// like callstacks and performance data
var internalModule = modules[namespace]._ = {};
// vars for tracking Promise API callbacks
internalModule.fh = {}; // fulfillmentHandler list
internalModule.eh = {}; // errorHandler list
internalModule.ph = {}; // progressHandler list
// generate the URL that we will download from (based on http/https)
internalModule.l = url ? url.replace(/^\/\//, (protocol=='https:' ? protocol : 'http:') + '//') : url;
// download performance tracking dictionary (keeps timestamps
// of each stage of the download for later analysis)
internalModule.p = {
0: +new Date
};
internalModule.P = function(f) {
internalModule.p[f] = new Date - internalModule.p[0]
};
// track the window.onload event
function windowLoadHandler() {
internalModule.P(load);
// use internalModule.w to remember that the onload event
// triggered, for future module imports
internalModule.w = 1;
modules[namespace]('_load')
}
// if the window.onload event triggered previously for any other
// namespace, trigger it again for this namespace
if (internalModule.w) windowLoadHandler();
// listen for onload
theWindow.addEventListener ? theWindow.addEventListener(load, windowLoadHandler, false) : theWindow.attachEvent("on" + load, windowLoadHandler);
// download the library (if a URL was given...otherwise we
// assume that something else is providing this namespace)
var downloadIntoFrameContext = function() {
// this helper is used to build the inner iframe where
// the module will live in its own window context
function buildInnerFrameHtml() {
return [
"<!DOCTYPE ",html,"><",html,"><head></head><",body,"><",script," src=\"", internalModule.l,"\"></",script,"></", body,"></",html,">",
].join("");
}
// try to get a handle on the document body
var body = "body",
script = "script",
html = "html",
documentBody = theDocument[body];
// if the document body does not exist yet, wait 100ms
// and retry this anonymous closure
if (!documentBody) {
return setTimeout(downloadIntoFrameContext, 100)
}
// performance tracking: we have reached stage 1 (building inner frame)
internalModule.P(1);
// use vars to refer to strings, this improves compression by
// allowing the compiler to treat repeated instances as one
var appendChild = "appendChild",
createElement = "createElement",
srcAttr = "src",
innerFrameWrapper = theDocument[createElement]("div"),
innerFrameContainer = innerFrameWrapper[appendChild](theDocument[createElement]("div")),
innerFrame = theDocument[createElement]("iframe"),
documentString = "document",
domain = "domain",
domainSrc,
contentWindow = "contentWindow";
// hide the iframe container and append it to the document
innerFrameWrapper.style.display = "none";
documentBody.insertBefore(innerFrameWrapper, documentBody.firstChild).id = lightningjsName + "-" + namespace;
innerFrame.frameBorder = "0";
innerFrame.id = lightningjsName + "-frame-" + namespace;
if (/MSIE[ ]+6/.test(navigator.userAgent)) {
// in IE6, we make sure to load javascript:false to avoid
// about:blank security warnings under SSL
innerFrame[srcAttr] = "javascript:false"
}
innerFrame.allowTransparency = "true";
innerFrameContainer[appendChild](innerFrame);
// Try to start writing into the blank iframe. In IE, this will fail if document.domain has been set,
// so fail back to using a javascript src for the frame. In IE > 6, these urls will normally prevent
// the window from triggering onload, so we only use the javascript url to open the document and set
// its document.domain
try {
innerFrame[contentWindow][documentString].open()
} catch(E) {
// keep track of the actual document.domain in the
// internal module in case it is useful in the future
internalModule[domain] = theDocument[domain];
domainSrc = "javascript:var d=" + documentString + ".open();d.domain='" + theDocument.domain + "';";
innerFrame[srcAttr] = domainSrc + "void(0);"
}
var loadScript = function(e) {
var iframeDocument = e.currentTarget.contentDocument;
var head = iframeDocument.getElementsByTagName('head')[0];
var script = iframeDocument.createElement('script');
script.setAttribute(srcAttr, internalModule.l);
head.appendChild(script);
};
// Set the HTML of the iframe. In IE 6, the document.domain from the iframe src hasn't had time to
// "settle", so trying to access the contentDocument will throw an error. Luckily, in IE 7 we can
// finish writing the html with the iframe src without preventing the page from onloading
try {
var frameDocument = innerFrame[contentWindow][documentString];
frameDocument.write(buildInnerFrameHtml());
frameDocument.close();
} catch(D) {
innerFrame[srcAttr] = domainSrc + 'd.write("' + buildInnerFrameHtml().replace(/"/g, String.fromCharCode(92) + '"') + '");d.close();'
}
// performance tracking: this is the last bit of code for
// the loader to execute, we want to know how long it took
internalModule.P(2)
};
internalModule.l && downloadIntoFrameContext();
})()
})();
// freeze the version identifier into this module
modules[moduleName].lv = lightningjsVersion;
// return the module itself
return modules[moduleName];
}
// load lightningjs as a module itself, this has the side benefit
// of making sure there is at least one module listening to window.onload
var lightningjs = window[lightningjsName] = require(lightningjsName);
// export the public lightningjs API
lightningjs.require = require;
lightningjs.modules = modules;
})({});