Skip to content

Instantly share code, notes, and snippets.

@quietlynn
Created May 9, 2012 15:51
Show Gist options
  • Save quietlynn/2645797 to your computer and use it in GitHub Desktop.
Save quietlynn/2645797 to your computer and use it in GitHub Desktop.
dependency_loader.user.js => Load everything needed in userscripts.
/*
dependency_loader.user.js => Load everything needed in userscripts.
Copyright (C) 2012 Jingqin Lynn
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
// ==UserScript==
// @name dependency_loader.user.js
// @namespace http://project.quietmusic.org/2012/userscript/utils/
// @description A dependency loader for userscripts.
// @match *://*/*
// @run-at document-start
// ==/UserScript==
(function () {
'use strict';
var win = window;
if (typeof(unsafeWindow) != 'undefined') {
//Chrome V8 don't support unsafeWindow. Time for a hack.
if (window == unsafeWindow) {
var span = document.createElement('span');
span.setAttribute('onclick', 'return window;');
unsafeWindow = span.onclick();
}
win = unsafeWindow;
}
if (win.DependencyLoader) return;
win.DependencyLoader = {};
win.DependencyLoader.state = 'initializing';
var wrapCallback = function (callback) {
if (typeof(callback) == 'string') {
return function() {
win.DependencyLoader.injectScript(callback);
};
} else {
return callback;
}
};
var documentReadyWaitList = null;
var injectOnDocumentReady = function (element) {
if (!documentReadyWaitList) {
documentReadyWaitList = [];
document.addEventListener('DOMContentLoaded', function () {
for (var i in documentReadyWaitList) {
document.head.appendChild(documentReadyWaitList[i]);
if (!documentReadyWaitList[i].src)
documentReadyWaitList[i].onload();
}
documentReadyWaitList = null;
}, false);
}
documentReadyWaitList.push(element);
};
var addScriptElement = function (element, success, error) {
element.type = element.type || 'text/javascript';
if (success && element.src) {
element.addEventListener('load', wrapCallback(success), false);
}
if (error) {
element.addEventListener('error', wrapCallback(error), false);
}
if (document.head) {
document.head.appendChild(element);
if (success && !element.src) wrapCallback(success)();
} else {
if (success && !element.src) {
element.onload = wrapCallback(success);
}
injectOnDocumentReady(element);
}
return element;
};
win.DependencyLoader.injectScript = function (script, success, error) {
var element = document.createElement('script');
element.textContent = script;
return addScriptElement(element, success, error);
};
win.DependencyLoader.loadScript = function (url, success, error) {
var element = document.createElement('script');
element.src = url;
return addScriptElement(element, success, error);
};
win.DependencyLoader.Dependency = function (name) {
this.name = name;
this.state = win.DependencyLoader.Dependency.LOADING;
this.loadCallbacks = [];
this.errorCallbacks = [];
this.holdCount = 0;
};
win.DependencyLoader.Dependency.LOADING = 'loading';
win.DependencyLoader.Dependency.DONE = 'done';
win.DependencyLoader.Dependency.ERROR = 'error';
win.DependencyLoader.Dependency.prototype.load = function () {
this.state = win.DependencyLoader.Dependency.DONE;
for (var i in this.loadCallbacks) {
this.loadCallbacks[i](this.name);
}
this.loadCallbacks = null;
this.errorCallbacks = null;
};
win.DependencyLoader.Dependency.prototype.error = function () {
this.state = win.DependencyLoader.Dependency.ERROR;
for (var i in this.errorCallbacks) {
this.errorCallbacks[i](this.name);
}
this.loadCallbacks = null;
this.errorCallbacks = null;
};
var allDependencies = {};
var optionalCallbacks = {};
win.DependencyLoader.dependencyState = function (name, state) {
var d = allDependencies[name];
if (!d) return null;
if (!state) return d.state;
return d.state = state;
};
win.DependencyLoader.hold = function (name) {
var d = allDependencies[name];
if (d) d.holdCount++;
};
win.DependencyLoader.release = function (name) {
var d = allDependencies[name];
if (d) {
d.holdCount--;
if (d.holdCount <= 0) {
d.load();
}
}
};
win.DependencyLoader._dependencyEnd = function (name) {
var d = allDependencies[name];
if (d.holdCount <= 0) {
d.load();
var callbacks = optionalCallbacks[name];
if (callbacks) {
callbacks.forEach(function (cb) {
cb(name);
});
delete optionalCallbacks[name];
}
}
};
win.DependencyLoader.reset = function (name) {
delete allDependencies[name];
};
win.DependencyLoader.require = function (name, source, success, error) {
var d = allDependencies[name];
if (!d || d.state == win.DependencyLoader.Dependency.ERROR) {
d = allDependencies[name] = new win.DependencyLoader.Dependency(name);
if (success) d.loadCallbacks.push(success);
if (error) d.errorCallbacks.push(error);
var loaderFunc = null;
if (typeof(source) == 'string') {
loaderFunc = win.DependencyLoader.loadScript;
} else {
loaderFunc = win.DependencyLoader.injectScript;
source = '(' + source.toString() + ')();';
}
loaderFunc(
source,
'window.DependencyLoader._dependencyEnd(' + JSON.stringify(d.name) + ');',
d.error.bind(d)
);
return true;
} else {
switch (d.state) {
case win.DependencyLoader.Dependency.DONE:
if (success) success(name);
break;
case win.DependencyLoader.Dependency.LOADING:
if (success) d.loadCallbacks.push(success);
if (error) d.errorCallbacks.push(error);
break;
}
return false;
}
};
win.DependencyLoader.requireAll = function (dependencies, success, error) {
if (dependencies.length == 0) {
if (success) success();
return;
}
var dependenciesLeft = 0;
var dependencyCallback = function (name) {
dependenciesLeft--;
if (dependenciesLeft <= 0 && success) {
success();
}
};
var d = null;
for (var name in dependencies) {
dependenciesLeft++;
win.DependencyLoader.require(name, dependencies[name], dependencyCallback, error);
}
};
win.DependencyLoader.requireOrdered = function (dependencies, success, error) {
var i = -1;
var loadNextGroup = function () {
i++;
if (i < dependencies.length) {
win.DependencyLoader.requireAll(dependencies[i], loadNextGroup, error);
} else {
if (success) success();
}
};
loadNextGroup();
};
win.DependencyLoader.optional = function (name, callback) {
if (win.DependencyLoader.dependencyState(name) ===
win.DependencyLoader.Dependency.DONE) {
callback(name);
return;
}
var callbacks = optionalCallbacks[name];
if (!callbacks) {
callbacks = optionalCallbacks[name] = [];
}
callbacks.push(callback);
};
if (win.DependencyLoaderPreload) {
for (var i in win.DependencyLoaderPreload) {
win.DependencyLoader.requireAll(
win.DependencyLoaderPreload[i].dependencies,
win.DependencyLoaderPreload[i].success,
win.DependencyLoaderPreload[i].error
);
}
}
win.DependencyLoader.state = 'ready';
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment