-
Notifications
You must be signed in to change notification settings - Fork 39
/
ReactScriptLoader.js
143 lines (128 loc) · 4.41 KB
/
ReactScriptLoader.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
// A dictionary mapping script URLs to a dictionary mapping
// component key to component for all components that are waiting
// for the script to load.
var scriptObservers = {};
// A dictionary mapping script URL to a boolean value indicating if the script
// has already been loaded.
var loadedScripts = {};
// A dictionary mapping script URL to a boolean value indicating if the script
// has failed to load.
var erroredScripts = {};
// A counter used to generate a unique id for each component that uses
// ScriptLoaderMixin.
var idCount = 0;
var ReactScriptLoader = {
componentDidMount: function(key, component, scriptURL) {
if (typeof component.onScriptLoaded !== 'function') {
throw new Error('ScriptLoader: Component class must implement onScriptLoaded()');
}
if (typeof component.onScriptError !== 'function') {
throw new Error('ScriptLoader: Component class must implement onScriptError()');
}
if (loadedScripts[scriptURL]) {
component.onScriptLoaded();
return;
}
if (erroredScripts[scriptURL]) {
component.onScriptError();
return;
}
// If the script is loading, add the component to the script's observers
// and return. Otherwise, initialize the script's observers with the component
// and start loading the script.
if (scriptObservers[scriptURL]) {
scriptObservers[scriptURL][key] = component;
return;
}
var observers = {};
observers[key] = component;
scriptObservers[scriptURL] = observers;
var script = document.createElement('script');
if (typeof component.onScriptTagCreated === 'function') {
component.onScriptTagCreated(script);
}
script.src = scriptURL;
script.async = 1;
var callObserverFuncAndRemoveObserver = function(func) {
var observers = scriptObservers[scriptURL];
for (var key in observers) {
var observer = observers[key];
var removeObserver = func(observer);
if (removeObserver) {
delete scriptObservers[scriptURL][key];
}
}
//delete scriptObservers[scriptURL];
}
script.onload = function() {
loadedScripts[scriptURL] = true;
callObserverFuncAndRemoveObserver(function(observer) {
if (observer.deferOnScriptLoaded && observer.deferOnScriptLoaded()) {
return false;
}
observer.onScriptLoaded();
return true;
});
};
script.onreadystatechange = function () {
if (this.readyState == 'complete' || this.readyState == 'loaded') {
script.onload();
}
};
script.onerror = function(event) {
erroredScripts[scriptURL] = true;
callObserverFuncAndRemoveObserver(function(observer) {
observer.onScriptError();
return true;
});
};
// (old) MSIE browsers may call 'onreadystatechange' instead of 'onload'
script.onreadystatechange = function() {
if (this.readyState == 'loaded') {
// wait for other events, then call onload if default onload hadn't been called
window.setTimeout(function() {
if (loadedScripts[scriptURL] !== true) script.onload();
}, 0);
}
};
document.body.appendChild(script);
},
componentWillUnmount: function(key, scriptURL) {
// If the component is waiting for the script to load, remove the
// component from the script's observers before unmounting the component.
var observers = scriptObservers[scriptURL];
if (observers) {
delete observers[key];
}
},
triggerOnScriptLoaded: function(scriptURL) {
if (!loadedScripts[scriptURL]) {
throw new Error('Error: only call this function after the script has in fact loaded.');
}
var observers = scriptObservers[scriptURL];
for (var key in observers) {
var observer = observers[key];
observer.onScriptLoaded();
}
delete scriptObservers[scriptURL];
}
};
var ReactScriptLoaderMixin = {
componentDidMount: function() {
if (typeof this.getScriptURL !== 'function') {
throw new Error("ScriptLoaderMixin: Component class must implement getScriptURL().")
}
ReactScriptLoader.componentDidMount(this.__getScriptLoaderID(), this, this.getScriptURL());
},
componentWillUnmount: function() {
ReactScriptLoader.componentWillUnmount(this.__getScriptLoaderID(), this.getScriptURL());
},
__getScriptLoaderID: function() {
if (typeof this.__reactScriptLoaderID === 'undefined') {
this.__reactScriptLoaderID = 'id' + idCount++;
}
return this.__reactScriptLoaderID;
},
};
exports.ReactScriptLoaderMixin = ReactScriptLoaderMixin;
exports.ReactScriptLoader = ReactScriptLoader;