/*
 * includer.js
 * $Revision: 1.18 $
 *
 * dependencies: jQuery
 *
 * (c) 1992-2009 Cisco Systems, Inc. All rights reserved.
 * Terms and Conditions: http://cisco.com/en/US/swassets/sw293/sitewide_important_notices.html
 */

if (typeof(cdc) == "undefined") cdc = new Object();
if (typeof(cdc.includer) == "undefined"){
	cdc.includer = {};
	if (typeof(cdc.includer.alreadyInPage) == 'undefined'){
		cdc.includer.alreadyInPage = [];
	}
	cdc.includer.widgets = {
		'module-sbt':{
			js: [
				'http://cisco.com/cdc_content_elements/visualsciences/visualsciences.js',
				'http://cisco.com/web/fw/j/sbt.js'
			],
			css: ['http://cisco.com/web/fw/c/sbt.css']
		},
                'widget-popular':{
			js: [
				'/web/fw/w/widget-popular.js',
				'http://cmsg-ws.cisco.com/js/discovery.js'
			]
		},
		'cdc-widget-accordion' : {
			js: [
				'http://www.cisco.com/cdc_content_elements/visualsciences/visualsciences.js',
				'/web/fw/lib/jquery.hoverIntent.minified.js',
				'/web/fw/w/accordion/widget-accordion.js'
			]
		},
		'cdc-widget-lightbox' : {
			js: [
			  '/web/fw/lib/jqmodal.js',
			  '/web/fw/lib/jquery.bgiframe.min.js',
			  'http://www.cisco.com/cdc_content_elements/flash/swfobject/swfobject.js',
			  '/web/fw/w/lightbox/lightbox.js'
			],
			css: [
			   '/web/fw/w/lightbox/lightbox.css'
			]
		},

		// Note: Component Library code is now in /web/fw/w/cl/cl.js
		'_default':{
			js: [
				//'/web/fw/w/cl/cl.min.js'
				'/web/fw/w/cl/cl.js'
			]
		}
	};

	cdc.includer.search = function(widgets){
		for (id in widgets){
			if (id == '_default' || document.getElementById(id)){
				if (widgets[id]['css'] && widgets[id]['css'].length){
					cdc.includer.loadCss(widgets[id]['css']);
				}
				if (widgets[id]['js'] && widgets[id]['js'].length){
					cdc.includer.loadJs(widgets[id]['js'],
						{callback:widgets[id]['callback']}
					);
				}
				else if (widgets[id]['callback']){
					(widgets[id]['callback'])();
				}
			}
		}
	};

	cdc.includer.uniquifyIncludes = function(includes,type) {
		var dedup = {};
		var deduped = [];
		var nodeName, attrName;
                if (type == 'js') {
			nodeName = 'script';
			attrName = 'src';
		}
		else if (type == 'css') {
			nodeName = 'link';
			attrName = 'href';
		}

		for (var i=0;i<includes.length;i++){
			dedup[includes[i]]=1;
		}
		if (typeof(cdc.includer.alreadyInPage) != 'undefined'){
			for (var i=0;i<cdc.includer.alreadyInPage.length;i++){
				dedup[cdc.includer.alreadyInPage[i]]=0;
			}
		}
		jQuery(nodeName).each(function(){
			if (this[attrName]){
				var uncachedUrl = this[attrName].replace(/http:\/\/[^\/]*/,'');
				uncachedUrl = uncachedUrl.replace(/[\&\?]cacheReset=[0-9\-]*/,'');
				dedup[uncachedUrl]=0;
			}
		});
		for (var i=0;i<includes.length;i++){
			if (dedup[includes[i]]){
				deduped.push(includes[i]);
			}
		}
		return deduped;
	};

	cdc.includer.loadCss = function(includes,extraArgs){
		if (typeof includes == "string") {
			includes = [ includes ];
		}
		if (!extraArgs){
			extraArgs = {};
		}

		includes = cdc.includer.uniquifyIncludes(includes,'css');
		for (var i=0;i<includes.length;i++){
			//NEED TO check for css in dom
			//include css if not there already
			var css = document.createElement('link');
			css.setAttribute('rel', 'stylesheet');
			css.setAttribute('type', 'text/css');
			css.setAttribute('href', includes[i]);
                        if (extraArgs.ids && extraArgs.ids[i]) {
				css.setAttribute('id', extraArgs.ids[i]);
                        }
			document.getElementsByTagName('head')[0].appendChild(css);
			cdc.debug.log("appended the stylesheet %o to the HEAD", includes[i]);
		}

	};

	cdc.includer.loadJs = function(includes,extraArgs){
		cdc.debug.log('includer: loadjs');
		cdc.debug.log('includer: includes is a '+typeof includes);
		if (typeof includes == "string") {
			includes = [ includes ];
		}
		else {
			cdc.debug.log('includer: it is '+includes.length+' long.');
			cdc.debug.log('includer: first element: '+includes[0]);
		}

		if (!extraArgs){
			extraArgs = {};
		}

		if (!extraArgs.hasBeenCleaned){
			includes = cdc.includer.uniquifyIncludes(includes,'js');
			extraArgs.hasBeenCleaned=1;
			if (includes.length==0){
				if (extraArgs.callback){
					cdc.debug.log('includer: there are no js files and there is a callback, doing it.');
					extraArgs.callback();
				}
				return;
			}
		}

		var currentFile = includes.shift();

		var scriptNode = document.createElement('script');
		document.getElementsByTagName("head")[0].appendChild(scriptNode);
		scriptNode.language='javascript';
		if (extraArgs.ids && extraArgs.ids.length) {
			scriptNode.id = extraArgs.ids.shift();
		}

		if (includes.length){
			cdc.debug.log('includer: there are more includes after this one so build in recursion using an event handler on the script node');
			if(document.attachEvent){//IE
				scriptNode.onreadystatechange = function(){
					if (this.readyState == "loaded") {
						cdc.includer.loadJs(includes,extraArgs);
					}
				}
			}
			else{
				scriptNode.onload = function(){cdc.includer.loadJs(includes,extraArgs);};
			}
		}
		else if (extraArgs.callback){
			cdc.debug.log('includer: there are no more includes after this one but there is a callback so put it on the script node as a handler');
			if(document.attachEvent){//IE
				scriptNode.onreadystatechange = function(){
					if (this.readyState == "loaded") {
						extraArgs.callback();
					}
				}
			}
			else{
				scriptNode.onload = function(){extraArgs.callback();};
			}
		}
		var src = currentFile;
		if (jQuery.browser.msie && !src.match(/noCacheBust/) && !extraArgs.noCacheBust){
                      src = cdc.includer.cacheBust(currentFile);
		}
		cdc.debug.log('includer: setting src on script node to '+src);
		scriptNode.src=src;
	};

	jQuery( function(){cdc.includer.search(cdc.includer.widgets)} );
}

// cache buster - puts a cache avoidance param on a url with a random number
// invoke with:
// cdc.includer.cacheBust('http://www.cisco.com');          = YourUrl?cacheReset=rand#
// cdc.includer.cacheBust('http://www.cisco.com','foo');    = YourUrl?foo=rand#
// cdc.includer.cacheBust('http://ng-prod1/image');         = YourUrl&cacheReset=rand#
// cdc.includer.cacheBust('http://cisco.com/edit.pl?a=3');  = YourUrl&cacheReset=rand#
  cdc.includer.cacheBust = function(url,param){
    if (!param) {param = 'cacheReset'};
    var delim = "?";
    // if url is ng-prod1(bam) or has ?, set param delimeter to &
    if (url.match(/(ng-prod1|\?)/)) {delim = "&"};
    var fullParam = delim+param+'=';
    // degug alert(url+fullParam+cdc_rand_num());
    return url+fullParam+cdc.includer.cdcRandNum();
  };

// randum number generator
// input: takes param limit to set random max, default is 1000
// output: 'TimeInSeconds-RandNum'
// invoke with: cdc.includer.randNum(50); or cdc_rand_num();
  cdc.includer.cdcRandNum = function(limit){
    if (!limit) {limit = 1000};
    var sNum = Math.floor(Math.random()*limit)+1;
    var sTime = (new Date).getTime();
    var rNum = sTime+"-"+sNum;
    return rNum;
  };






if (typeof (cdc.util) == "undefined") cdc.util = {};

cdc.util.JsLoader = new function () {

    var _loadedUrls = null; // Map: loaded url -> 1, initially null to enable initialization
    var _pendingQueue = []; // Array: {url: pendingUrlString, callbacks: [ array of callbacks] }

    
    var _doLoad = function (jsUrls, callback, forceLoad, dataType) {
        if (_loadedUrls == null) _initialize();
        var _forceLoad = !!forceLoad;
        var _dataType = dataType;
        var processUrlAtIndex = function (jsUrls, index) {
            var addedCount = 0;
            if (index >= jsUrls.length) { // No more assets to load for this request
                if (typeof (callback) == "function") callback();
                return;
            }
            var url = jsUrls[index];
            if (typeof (_loadedUrls[url]) == "undefined" || _forceLoad) { // This asset is not yet loaded. Add it to the pending queue if needed.
                var ndx = index;
                var assetLoadedCallback = function () { processUrlAtIndex (jsUrls, ndx+1);  };
                var  pos = 0, m = _pendingQueue.length;
                for (; pos < m; pos++) {
                    if (_pendingQueue[pos].url === url) {
                        break;
                    }
                }
                if (pos >= m) { // This URL is not in the pending queue, so add it to the queue.                    
                    _pendingQueue.push ({ "url": url, "callbacks": [ assetLoadedCallback ], okToUseCache: true, dataType: _dataType });
                    addedCount = 1;
                } else { // This URL is already on the pending queue
                    _pendingQueue[pos].callbacks.push (assetLoadedCallback);
                }
            } else { // Already loaded, so ignore it and move on.
                addedCount += processUrlAtIndex (jsUrls, index+1);
            }
            return addedCount;
        };
        // Prime the pump
        jsUrls = (typeof (jsUrls) == "string") ? [ jsUrls ] : [].concat (jsUrls); // If it's an array, make a copy of it
        var queueWasEmpty = _pendingQueue.length <= 0;
        if (processUrlAtIndex (jsUrls, 0) > 0 && queueWasEmpty) {
            // The queue was initially empty, but I added stuff to it
            _service();
        }
        return this;
    };

    /**
     * Load the given list of JavaScript assets, in the order specified. If the callback is provided, call it after all the assets have been loaded. This
     * function takes care of multiple interleaved calls that might all specify common assets. If two callers specify assets in different orderings, the
     * ordering of the first such caller wins.
     * @param jsUrls an array of strings specifying JavaScript asset URLs
     * @param callback (optional) a function to call when all the specified assets have been loaded.
     * @param forceLoad (optional) boolean: true if the resource should be loaded even if it was already loaded earlier
     */
    this.load = function (jsUrls, callback, forceLoad) {
        if ((!jsUrls || jsUrls.length <= 0) && typeof (callback) == "function") {
            // No urls given, but callback specified, so the needed assets are (vacuously) loaded. So call the callback.
            callback();
            return;
        }
        _doLoad (jsUrls, callback, forceLoad, "script");
        return this;
    }


    /**
     * Load just one URL from a data service, with an optional parameter set. This method expects JSONP as the payload. This method caters to a common use case.
     */
    this.loadOne = function (jsUrl, parameters, callback, forceLoad) {
        var url = jsUrl;
        if (parameters) {
            var separator = (jsUrl.indexOf ("?") > 0) ? "&" : "?";
            url += separator + jQuery.param (parameters);
        }
        return _doLoad ([ url ], callback, forceLoad, "json");
    };
    

    /**
     * Tell this loader to pretend that the given URLs are already loaded into the page, so they don't need to be reloaded.
     * @param jsUrls an array of asset URLs
     */
    this.assumeAlreadyLoaded = function (jsUrls) {
        jQuery.each (jsUrls, function (item) {
            _loadedUrls[item] = 1;
        });
    };

    // Private functions
    function _initialize () {
        // Remember the scripts loaded via explicit script tags
        _loadedUrls = {};
        jQuery("script").each (function (item) {
            var src = this.getAttribute ("src");
            if (src) _loadedUrls[src] = 1;
        });
    };
    
    function _service () {
        // Service one request, and invoke pseudo-recursively
        if (_pendingQueue.length >= 1) {
            var item = _pendingQueue[0];
            jQuery.ajax({
                type: "GET",
                url: item.url,
                dataType: item.dataType || "script",
                cache: item.okToUseCache,
                success: function (data) {
                    _loadedUrls[item.url] = 1;
                    _pendingQueue.shift();
                    for (var i = 0; i < item.callbacks.length; i++) {
                        item.callbacks[i]();
                    }
                    _service();
                }
            });
        }
    };

    return this;
};


// /**
//  * Redefining includer to use JsLoader:
//  */
// cdc.includer.loadJs = function (includes,extraArgs) {
//     var forceLoad = extraArgs && !!extraArgs.noCacheBust;
//     var callback = extraArgs && typeof (extraArgs.callback) == "function" ? extraArgs.callback : null;
//     return cdc.util.JsLoader.load (includes, callback, forceLoad);
// };






/**
 * A request queue that ensures that a new request is sent out to a server only after all previous requests in the queue have been handled.
 * Requests added to the queue will be processed in order, and the i-th request
 * will be sent to the server only after requests 0 through i-1 have finished processing.
 *
 * Example usage:
 *
 *    var myQueue = new cdc.util.ServiceRequestQueue();
 *    myQueue.addRequest ("http://me.com/myservice", {"foo": "1", "bar": "three"}, function () { ... });
 *
 */
cdc.util.ServiceRequestQueue = function () {

    var _queue = [], _pending = false, _loadedUrls = {} /* For de-duplication of assets */;

    /**
     * addRequest: Add a request for a data service.
     * @param url The URL of the service request
     * @param parameters the parameter set for the request: a map of strings to strings
     * @param callback the callback function to call when the request has been completed. This function will be called with the JSON data returned by the request.
     * @param okToUseCache (optional) a boolean value, set to true if the request may use an available cached version, i.e., a cache buster should be included; defaults to false
     * @param dataType  (optional) either "script" or "json" depending on the expected data type; defaults to "json"
     * This method is oriented towards a data service, hence its "okToUseCache" defaults to false.
     */
    this.addRequest = function (url, parameters, callback, dataType, okToUseCache) {
        var dataTypeToUse = dataType  || "json";
        if (typeof (_loadedUrls[url]) == "undefined" || !okToUseCache) {
            _queue.push ({"url": url, "parameters": parameters, "callback": callback, "okToUseCache": !!okToUseCache, "dataType": dataTypeToUse});
            _loadedUrls[url] = 1;
            _service();
        }
    }


    /**
     * Return the number of requests currently in this queue.
     */
    this.getQueueSize = function () {
        return _queue.length;
    }


    // Private functions
    function _service () {
        // Service one request, and invoke pseudo-recursively
        if (_queue.length >= 1 && !_pending) {
            _pending = true;
            var item = _queue[0];
            jQuery.ajax({
                type: "GET",
                url: item.url,
                data: item.parameters,
                dataType: item.dataType,
                cache: item.okToUseCache,
                success: function (data) {
                    item.callback (data);
                    _pending = false;
                    _queue.splice (0, 1);
                    _service();
                }
            });
        }
    }

};

