Created
June 29, 2010 21:47
-
-
Save bmeck/457867 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module.exports = {a: 'async'} | |
console.log('in >> '+__filename) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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') |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(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) | |
})(); | |
})(); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
exports.a = 'sync' | |
console.log('in >> '+__filename) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<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