Skip to content

Instantly share code, notes, and snippets.

@bmeck
Created June 29, 2010 21:47
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bmeck/457867 to your computer and use it in GitHub Desktop.
Save bmeck/457867 to your computer and use it in GitHub Desktop.
module.exports = {a: 'async'}
console.log('in >> '+__filename)
console.log('starting module.js')
require('async_test',function(exports){console.log('async_test.js loaded, a='+exports.a)})
console.log('sync_test.js loaded, a='+require('sync_test').a)
require.addListener('loaded',function(){console.log('require.loaded event')})
console.log('leaving module.js')
(function() {
//cant access anything really besides the true global, but then again, should be ok, has to specify the global to set an attr
var moduleFunction = function() {
with({}) {
return Function.apply(this,arguments);
}
}
;(function(){
var prevented={},stopped={};
var EventEmitter=function(){
var eListeners={};
$this=this===(function(){return this})()?{}:this
if(!$this.addListener){
$this.addListener=function(evtType,evtHandler) {
if(!eListeners[evtType]) {
eListeners[evtType]={};
}
eListeners[evtType][evtHandler]=evtHandler;
};
}
if(!$this.removeListener){
$this.removeListener=function(evtType,evtHandler) {
evtType=eListeners[evtType];
if(evtType){
if(evtType[evtHandler]) {
delete evtType[evtHandler];
return $this;
}
}
};
}
$this.emit=function(evt) {
var listeners = listeners=eListeners[evt]
for(handler in listeners) {
listeners[handler](Array.prototype.slice.call(arguments,1));
}
};
return $this;
};
//$1 user
//$2 pass
//$3 protocol
//$4 domain
//$5 path
//$6 filename
//$7 fileext
//$8 query
//$9 hash
var uriMatcher=RegExp(
"^"
+"(?:"
+"(?:([^/]*)"
+"(?:\\:([^/]))\\@)?"
+"([^\\:]+)\\:(?:[/]*)"
+")?"
+"([^/\\?\\#]*)?"
+"(?:/((?:[^\\?\\#/]*/)*))?"
+"([a-zA-Z0-9\\-\\_]+(?:\\.(?:[a-zA-Z0-9\\-\\_]+))*?)?"
+"(?:\\.([a-zA-Z0-9\\-\\_]+))?"
+"(?:\\?([^\#]*))?"
+"(?:\\#(.*))?"
+"$"
)
function URI(url) {
if(url instanceof URI) return url;
//if(url instanceof String == false) console.warn("Converting non-string url to string for URI parsing");
//Log.log("?",url)
url=String(url);//working with strings ppl~
//normalize the slashes
url=url.replace("\\","/")
//Log.log(1)
var match=url.match(uriMatcher)
//Log.log(2)
//console.error(url,match)
if(!match) throw Error("@Loader: URL:"+url+"appears to be invalid.")
//check, do we have something to work on ?
if(!(match[3]||match[4]||match[5]||match[7]||match[8]||match[9])) console.error("Url provided does not appear to be valid");
this.href=url
this.user=match[1]
this.password=match[2]
this.protocol=match[3]
this.domain=match[4]
//if it has no extension assume it is a directory
this.path=match[5]?match[5]:""
if(match[7]){
this.file={}
this.file.name=match[6],
this.file.extension=match[7]
}
this.query=match[8]
this.hash=match[9]
if(!this.protocol) {
if(this.domain) {
this.path=this.domain+"/"+this.path;
this.domain=undefined;
}
}
if(!this.file) {
if(match[6]) {
this.path+=match[6]+"/"
}
}
//Log.log("URI",this)
return this;
}
function ResolveUri(base,target){
base=new URI(base);
//console.log(base,target)
target=new URI(target);
//Log.log(target,base)
if(target.protocol||target.domain) {
return target;
}
var currentPathing=[]//dont jump up a domain
//if target has the root selected dont concat our path
if(base.path&&target.path.charAt(0)!="/") currentPathing=currentPathing.concat(base.path.split("/"))
//Log.log("pathing",currentPathing.concat(),base)
if(currentPathing[currentPathing.length-1]=="") currentPathing.pop();
var targetPathing=[]
if(target.path) targetPathing=targetPathing.concat(target.path.split("/"))
for(var i=0;i<targetPathing.length;i++) {
var dir=targetPathing[i];
if(dir=="..") {
if(currentPathing.length==0) {
//console.warn("Cannot go to ../ of a domain");
}
else {
//console.log("GOING UP")
currentPathing.pop();
}
}
else if(dir.length>=1) {
currentPathing.push(dir);
}
}
var href=(base.user||"")+
(base.password||"")+
(base.protocol?(base.protocol)+"://":"")+
(base.domain?(base.domain):"")+
//if it has no extension assume it is a directory
("/"+currentPathing.join("/"))+
(target.file?"/"+(target.file.name)+"."+(target.file.extension):"")+
(target.query||"")+
(target.hash||"")
//Log.log("resolved to",href,"from",base,target)
var resolvedUri = new URI(
href
)
return resolvedUri;
}
//@start event queue
var EventQueue = function(){
//Form an event queue (needed due to a slew of delayed action events)
//ie. ajax, saving, undo, copy, paste, etc.
var self = this;
var eventQueue = this.eventQueue = [];
var eventHolds = this.eventHolds = {};
//Attempt to perform all the events in the queue
//Returns false if stopped by something
//Otherwise, returns true
this.Flush = function() {
while (eventQueue.length > 0)
{
for (var hold in eventHolds) {
return false;
}
var event = eventQueue.shift();
if (!event.thisObject) {
event.thisObject = {};//Default to avoid global namespace colisions
}
event.callbackFunction.apply(event.thisObject,event.argumentsArray);
}
return true;
}
//Put an event on the queue
//@evt = {
// argumentsArray = [],
// callbackFunction = function(...){},
// thisObject = $
//}
//@return position in queue (1 indexed) or null if not allowed
this.PushEvent = function(evt) {
for (var filter in eventHolds) {
if(eventHolds[filter] && !eventHolds[filter]()) return null;
}
return eventQueue.push(evt)
}
//Put an event on the queue
//@evt = {
// argumentsArray = [],
// callbackFunction = function(...){},
// thisObject = $
//}
//@return position in queue (1 indexed) or null if not allowed
this.UnshiftEvent = function(evt) {
var eventFilter;
for (var filter in eventHolds) {
if (eventFilter && !eventFilter(evt)) {
//console.log("FAIL")
return null;
}
}
eventQueue.unshift(evt);
//Log.log(evt,"added")
return 0;
}
//Put up a mutex lock to prevent any more events
//@pushFilter - bool function(evt) - return true if push is allowed for this event
//@return id of the mutex in order to release it
this.HoldEvents = function(pushFilter) {
var holdId = Math.random();
while (eventHolds[holdId]) {
holdId = Math.random();
}
eventHolds[holdId] = pushFilter;
return holdId;
}
//Release a mutex holding up the event queue
//@holdId a value returned from HoldEvents() of the mutex to nullify
this.ReleaseHold = function(holdId) {
//Log.log("releasing ",holdId,eventHolds)
if (holdId in eventHolds) {
delete eventHolds[holdId];
return true;
}
return false;
}
this.Bump = function(item) {
var index = eventQueue.indexOf(item)
if(index!=-1) {
var newTop = eventQueue.splice(index,1)[0];
this.UnshiftEvent(newTop)
}
}
// window.addEventListener("onload",function() {
// Log.log("Document Load Event Flush.")
// self.Flush();
// });
return this;
}
//@end event queue
var ScriptQueue = new EventQueue();
var CallbackQueue = new EventQueue();
var LoadingScripts = {}
var LoadedScripts = {}
var CurrentDir;
/*
Class: Loader
Provides a means to load javascript asynchronously.
Scripts loaded with this gain access to the logger using Log instead of console in order to keep line numbers.
*/
/*
Function: require
Used in order to load a script at a given url and evaluate it with some minor protection.
Scripts are given to an EventQueue for loading and once loaded
are given to another EventQueue to be evaluated in proper order.
Recursive scripts work fine.
Parameters:
url - URI relative to the current script's document to load
force - allow the script to be reloaded even if we have done so already
(does not force a reload if the script is already waiting to be evaluated)
See:
<EventQueue>
<URI>
*/
var require = window['require'] = function require(url,callback_or_force,force) {
if(!force) {
if(!callback_or_force instanceof Function) {
force=callback_or_force
}
}
if(!CurrentDir) {
//Log.log("defaulting currentDir")
CurrentDir=new URI(document.location.href)
}
//Log.log("@Loader: Loading require... ",url,CurrentDir.href)
if(url.length<3) {
url+=".js"
}
url=new URI(url);
if(!url.file) { //defaults to index.js
ResolveUri(url,"index.js")
}
else{
if(!url.file.extension) {
url.href+=".js";
url.file.extension="js"
}
}
var uri=ResolveUri(CurrentDir,url);
//Log.log("321",uri)
if(uri.href in LoadedScripts) {
if(!force) {
//Log.log("@Loader:",url,"already loaded, not forced, stopping...");
return LoadedScripts[uri.href];
}
}
var evt = {
callbackFunction: function(script){
if(uri.href in LoadedScripts) {
if(!force) {
return LoadedScripts[uri.href];
}
}
//console.log("@Loader: Synchronously Evaluating ",url)
var module = {}
var exports = {}
module.exports=exports
var scriptFunc=moduleFunction("module","exports","process","require","__filename","__dirname",script);
//alert(scriptFunc.toString())
scriptFunc.call({},module,exports,{},require,uri.href,uri.path);
//alert(exports.a)
delete LoadingScripts[uri.href];
return LoadedScripts[uri.href]=module.exports;
},
thisObject: null
}
var req = new XMLHttpRequest();
if(callback_or_force && callback_or_force instanceof Function) {
var oldCallback = evt.callbackFunction
evt.callbackFunction = function(script) {
callback_or_force(oldCallback(script))
}
//register it as loading
ScriptQueue.UnshiftEvent(evt);
if(uri.href in LoadingScripts) {
//Bump it if its already queued!
//Log.log("@Loader: New request for",uri.href,"bumping to highest priority.");
ScriptQueue.Bump(LoadingScripts[uri.href])
return;
}
LoadingScripts[uri.href]=evt;
//Log.log(LoadedScripts,LoadingScripts)
var callbackHold = CallbackQueue.HoldEvents();
//Log.log("@Loader: Holding callbacks for ",url,"with",callbackHold)
var hold = ScriptQueue.HoldEvents();
req.open('GET', uri.href, true);
req.onreadystatechange = function(xhrEvt) {
if (req.readyState == 4) {
if (req.status >= 400) {
throw Error("require could not load required script ", uri.href);
}
evt.argumentsArray = [req.responseText]
ScriptQueue.ReleaseHold(hold);
if(ScriptQueue.Flush()) {
CallbackQueue.ReleaseHold(callbackHold);
if(CallbackQueue.Flush()) {
require.emit('loaded')
}
}
}
}
req.send();
//CallbackQueue.UnshiftEvent({
// callbackFunction: callback_or_force
//});
}
else {
//done syncronously, must be smexecuted now
req.open('GET', uri.href, false);
req.send();
if (req.status >= 400) {
throw Error("require could not load required script ", uri.href);
}
return evt.callbackFunction(req.responseText)
}
}
EventEmitter.call(require)
})();
})();
exports.a = 'sync'
console.log('in >> '+__filename)
<html>
<head>
<title></title>
<script src='require.js'></script>
<script src='module.js'></script>
</head>
<body onload="console.log('window.onload event')">
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment