/*----------------------------------------------------------------

Class: adobe

Properties:
console - undefined
debugging - boolean
options - hash
version - string
COLOR_DEPTH - number or NaN
DIR - string
FILE - string
PATH - string
SCRIPT_BUILD - number or NaN
SCRIPT_ENGINE - string
SCRIPT_VERSION - number or NaN

----------------------------------------------------------------*/

var adobe = Class.create({
	initialize: function(options) {
		var src = document.getElementById("adobe").getAttribute("src"),
		lastfolder = src.lastIndexOf("/"),
		folders = src.substring(0, lastfolder),
		file = src.substring(lastfolder+1),
		q = src.split("?")[1],
		options = options || {},
		NAN = parseInt("");

		if(q) { Object.extend(options, q.toQueryParams()); }

		this.version = "0.1";
		this.options = options;
		this.debugging = false;
		this.console;
		this.DIR = folders;
		this.PATH = folders+"/";
		this.FILE = file;
		this.COLOR_DEPTH = screen.colorDepth || NAN;
		this.SCRIPT_ENGINE = (window.ScriptEngine) ? window.ScriptEngine() : "JavaScript";
		this.SCRIPT_VERSION = (this.SCRIPT_ENGINE == "JScript") ? (ScriptEngineMajorVersion()+ScriptEngineMinorVersion()/10) : NAN;
		this.SCRIPT_BUILD = (this.SCRIPT_ENGINE == "JScript") ? ScriptEngineBuildVersion() : NAN;
	},
/*----------------------------------------------------------------	

	Method: debug
	Set to true, this provides <Console> methods in the adobe object eg. adobe.log

>	adobe.debug(true)

	Parameters:
	bool - boolean
	
	Returned Value:
	Nothing

----------------------------------------------------------------*/
	debug: function(bool) {
		if(this.debugging === bool) {return;}
		this.debugging = !!bool;
		adobe.Jsan.errorLevel = (this.debugging) ? "die" : "none";
		adobe.Jsan.use("adobe.Console");
		var console = new adobe.Console();		
		Object.extend(this, console);
	}
});

adobe = new adobe();

/*----------------------------------------------------------------	

	JSAN implementation

----------------------------------------------------------------*/

adobe.Jsan = function () { adobe.Jsan.addRepository(arguments) }

adobe.Jsan.VERSION = 0.10;

adobe.Jsan.globalScope   = self;
adobe.Jsan.includePath   = ['.', 'lib'];
adobe.Jsan.errorLevel    = "none";
adobe.Jsan.errorMessage  = "";
adobe.Jsan.loaded        = {};

adobe.Jsan.use = function () {
    var classdef = adobe.Jsan.require(arguments[0]);
    if (!classdef) return null;

    var importList = adobe.Jsan._parseUseArgs.apply(adobe.Jsan, arguments).importList;
    adobe.Jsan.exporter(classdef, importList);

    return classdef;
}

adobe.Jsan.require = function (pkg) {
    var path = adobe.Jsan._convertPackageToPath(pkg);
    if (adobe.Jsan.loaded[path]) {
        return adobe.Jsan.loaded[path];
    }

    try {
        var classdef = eval(pkg);
        if (typeof classdef != 'undefined') return classdef;
    } catch (e) { /* nice try, eh? */ }


    for (var i = 0; i < adobe.Jsan.includePath.length; i++) {
        var js;
        try{
            var url = adobe.Jsan._convertPathToUrl(path, adobe.Jsan.includePath[i]);
                js  = adobe.Jsan._loadJSFromUrl(url);
        } catch (e) {
            if (i == adobe.Jsan.includePath.length - 1) throw e;
        }
        if (js != null) {
            var classdef = adobe.Jsan._createScript(js, pkg);
            adobe.Jsan.loaded[path] = classdef;
            return classdef;
        }
    }
    return false;

}

adobe.Jsan.exporter = function () {
    adobe.Jsan._exportItems.apply(adobe.Jsan, arguments);
}

adobe.Jsan.addRepository = function () {
    var temp = adobe.Jsan._flatten( arguments );
    // Need to go in reverse to do something as simple as unshift( @foo, @_ );
    for ( var i = temp.length - 1; i >= 0; i-- )
        adobe.Jsan.includePath.unshift(temp[i]);
    return adobe.Jsan;
}

adobe.Jsan._flatten = function( list1 ) {
    var list2 = new Array();
    for ( var i = 0; i < list1.length; i++ ) {
        if ( typeof list1[i] == 'object' ) {
            list2 = adobe.Jsan._flatten( list1[i], list2 );
        }
        else {
            list2.push( list1[i] );
        }
    }
    return list2;
};

adobe.Jsan._convertPathToUrl = function (path, repository) {
    return repository.concat('/' + path);
};
    

adobe.Jsan._convertPackageToPath = function (pkg) {
    var path = pkg.replace(/\./g, '/');
        path = path.concat('.js');
    return path;
}

adobe.Jsan._parseUseArgs = function () {
    var pkg        = arguments[0];
    var importList = [];

    for (var i = 1; i < arguments.length; i++)
        importList.push(arguments[i]);

    return {
        pkg:        pkg,
        importList: importList
    }
}

adobe.Jsan._loadJSFromUrl = function (url) {
    return new adobe.Jsan.Request().getText(url);
}

adobe.Jsan._findExportInList = function (list, request) {
    if (list == null) return false;
    for (var i = 0; i < list.length; i++)
        if (list[i] == request)
            return true;
    return false;
}

adobe.Jsan._findExportInTag = function (tags, request) {
    if (tags == null) return [];
    for (var i in tags)
        if (i == request)
            return tags[i];
    return [];
}

adobe.Jsan._exportItems = function (classdef, importList) {
    var exportList  = new Array();
    var EXPORT      = classdef.EXPORT;
    var EXPORT_OK   = classdef.EXPORT_OK;
    var EXPORT_TAGS = classdef.EXPORT_TAGS;
    
    if (importList.length > 0) {
       importList = adobe.Jsan._flatten( importList );

       for (var i = 0; i < importList.length; i++) {
            var request = importList[i];
            if (   adobe.Jsan._findExportInList(EXPORT,    request)
                || adobe.Jsan._findExportInList(EXPORT_OK, request)) {
                exportList.push(request);
                continue;
            }
            var list = adobe.Jsan._findExportInTag(EXPORT_TAGS, request);
            for (var i = 0; i < list.length; i++) {
                exportList.push(list[i]);
            }
        }
    } else {
        exportList = EXPORT;
    }
    adobe.Jsan._exportList(classdef, exportList);
}

adobe.Jsan._exportList = function (classdef, exportList) {
    if (typeof(exportList) != 'object') return null;
    for (var i = 0; i < exportList.length; i++) {
        var name = exportList[i];

        if (adobe.Jsan.globalScope[name] == null)
            adobe.Jsan.globalScope[name] = classdef[name];
    }
}

adobe.Jsan._makeNamespace = function(js, pkg) {
    var spaces = pkg.split('.');
    var parent = adobe.Jsan.globalScope;
    eval(js);
    var classdef = eval(pkg);
    for (var i = 0; i < spaces.length; i++) {
        var name = spaces[i];
        if (i == spaces.length - 1) {
            if (typeof parent[name] == 'undefined') {
                parent[name] = classdef;
                if ( typeof classdef['prototype'] != 'undefined' ) {
                    parent[name].prototype = classdef.prototype;
                }
            }
        } else {
            if (parent[name] == undefined) {
                parent[name] = {};
            }
        }

        parent = parent[name];
    }
    return classdef;
}

adobe.Jsan._handleError = function (msg, level) {
    if (!level) level = adobe.Jsan.errorLevel;
    adobe.Jsan.errorMessage = msg;

    switch (level) {
        case "none":
            break;
        case "warn":
            alert(msg);
            break;
        case "die":
        default:
            throw new Error(msg);
            break;
    }
}

adobe.Jsan._createScript = function (js, pkg) {
    try {
        return adobe.Jsan._makeNamespace(js, pkg);
    } catch (e) {
        adobe.Jsan._handleError("Could not create namespace[" + pkg + "]: " + e);
    }
    return null;
}


adobe.Jsan.prototype = {
    use: function () { adobe.Jsan.use.apply(adobe.Jsan, arguments) }
};


// Low-Level HTTP Request
adobe.Jsan.Request = function (jsan) {
    if (adobe.Jsan.globalScope.XMLHttpRequest) {
        this._req = new XMLHttpRequest();
    } else {
        this._req = new ActiveXObject("Microsoft.XMLHTTP");
    }
};

adobe.Jsan.Request.prototype = {
    _req:  null,
    
    getText: function (url) {
        this._req.open("GET", url, false);
        try {
            this._req.send(null);
            if (this._req.status == 200 || 
		this._req.status == 0)
                return this._req.responseText;
        } catch (e) {
            adobe.Jsan._handleError("File not found: " + url);
            return null;
        };

        adobe.Jsan._handleError("File not found: " + url);
        return null;
    }
};

Object.extend(adobe, {
/*----------------------------------------------------------------	

	Method: use

>	adobe.use("adobe.foo")

	Parameters:
	namespace - string
	
	Returned Value:
	Class reference
	
	Delegate for:
	<http://www.openjsan.org/src/c/cw/cwest/JSAN-0.08/doc/html/JSAN.html>

----------------------------------------------------------------*/
	use: adobe.Jsan.use,
/*----------------------------------------------------------------	

	Method: addRepository
	
>	adobe.addRepository("/path/to/repository");
	
	Parameters:
	path - string
	
	Returned Value:
	JSAN Object reference
	
	Delegate for:
	<http://www.openjsan.org/src/c/cw/cwest/JSAN-0.08/doc/html/JSAN.html>

----------------------------------------------------------------*/
	addRepository: adobe.Jsan.addRepository,
/*----------------------------------------------------------------	

	Method: setLoaded

>	adobe.setLoaded(adobe.foo, adobe.PATH+"adobe/foo.js")

	Parameters:
	module - object reference
	file - string
	
	Returned Value:
	None
	
	Delegate for:
	<http://www.openjsan.org/src/c/cw/cwest/JSAN-0.08/doc/html/JSAN.html>

----------------------------------------------------------------*/
	setLoaded: function(module, file) {
		adobe.Jsan.loaded[file] = module;
	}
});

adobe.addRepository(adobe.DIR);
adobe.setLoaded(adobe, adobe.FILE);

/*----------------------------------------------------------------	

	Asset Linking

----------------------------------------------------------------*/

/*	ASSET LOADER $Revision: 1.1 $
	Work in progress
	@author btapley
*/

/*	
	Class: Loader 
	Load assets into the document, prevent overlapping assets form being written more than once.
	
	Example:
>	adobe.Loader.requireAsset("/path/to/my/file.js");
>	adobe.Loader.requireAsset("_/library_path/to/my/file.css");
>	adobe.Loader.requireAsset("/path/to/my/file_print.css", { media: "print" });
*/

adobe.Loader = (function() {	
	var ATTR_TOKEN = "#ATTR#",
		STATUS_NONE = 0,
		STATUS_DONE = 1,
		STATUS_ERROR = 2,
		SRC_PATH_TRIG = "_/",
		PATH_CAPTURE = /(^.+\.)(\w+)(\?[^$]*$|$)/,
		SCRIPT_TAG = "<script #ATTR#><\/script>",
		LINK_TAG = "<link #ATTR# \/>",
		jscompress = !!adobe.jscompress,
		compress_path = adobe.jscompress_path,
		renderStatus = {},
		assets = {
			JS: [ SCRIPT_TAG, "src", {
				type:"text/javascript"
			}],
			CSS: [ LINK_TAG, "href", {
				type:"text/css",
				rel:"stylesheet"
			}]
		},
		renderAsset = function(path, user_attributes) {	
			var explode = path.match(PATH_CAPTURE), //break apart the path argument
				ext = explode[2], //file extension
				q = explode[3]; //query
			
			if(!ext) { return; } //didn't find a suitable file extension?
				
			var type = ext.toUpperCase(), //declare file type
				data = assets[type]; //declare data point
				
			if(!data) { return; } //is asset type defined in here?
				
			/* compression hack here. Still implementing server compression */
			if(type == "JS" && jscompress) {
				path = explode[1] + compress_path + "." + ext + q;
			}
				
			var out = {},
				attrs = [],
				attrN = "",
				code = data[0],
				pathAtt = data[1],
				reqAtt = data[2];
				
			for(attrN in reqAtt) { //copy required attributes 
				out[attrN] = reqAtt[attrN];
			}
			
			out[pathAtt] = path; //set path attribute
			
			if(user_attributes) { //copy user-defined attributes
				for(attrN in user_attributes) {
					out[attrN] = user_attributes[attrN];
				}
			}
			
			for(attrN in out) { //create attribute text eg. name="value"
				attrV = out[attrN];				
				attrs.push((attrV) ? (attrN + '="' + attrV + '"') : attrN);
			}
			
			return code.replace(ATTR_TOKEN, attrs.join(" "));
		};
	
	return {
		/*
			Function: requireAsset
			
			Parameters:
			path - location string (Paths beginning with "_/" will be relative to the library location)
			user_attributes - object instance (optional)
			
			Returns:
			Integer indicating render status (0=None, 1=Done, 2=Error)
		*/
		
		requireAsset : function(path, user_attributes) {
			if(!path) { return STATUS_NONE; } //insurance from bad calls
			
			if(path.indexOf(SRC_PATH_TRIG) === 0) { //did we request a library relative path?
				path = path.replace(SRC_PATH_TRIG, adobe.PATH); //replace the trigger with the path
			}
			
			var currentStatus = (renderStatus[path] || STATUS_NONE); //declare status?
			
			if(currentStatus > STATUS_NONE) { return currentStatus; } //this path was already written, terminally failed, or in progress?
								
			var txt = renderAsset(path, user_attributes);
			
			if(!txt) { 
				return (renderStatus[path] = STATUS_ERROR);
			} else {
				renderStatus[path] = currentStatus = STATUS_DONE; //new request, log it before writing to prevent recursion
			}
			
			document.write(txt);
			
			return currentStatus;
		},
		setLoaded: function(path) {
			renderStatus[path] = STATUS_DONE;
		}
	};
})();

Object.extend(adobe, {
/*----------------------------------------------------------------	

	Method: link
	
>	adobe.link("path/to/file.css", {media:"screen"})
	
	Parameters:
	path - relative path from adobe.js file as string
	params - hash
	
	Returned Value:
	0 = Nothing, 1 = Done, 2 = Error

----------------------------------------------------------------*/
	link: adobe.Loader.requireAsset.wrap(function($proceed, path, params) {
		$proceed(adobe.PATH + path, params);	
	}),
/*----------------------------------------------------------------	

	Method: setLinked

>	adobe.setLinked("/path/to/file.css")

	Parameters:
	path - relative path from adobe.js file as string
	
	Returned Value:
	None

----------------------------------------------------------------*/
	setLinked: adobe.Loader.setLoaded.wrap(function($proceed, path) {
		$proceed(adobe.PATH + path);
	})
});
