//    Licensed Materials - Property of IBM
//
//    IBM Cognos Products:  cpscrn

//
//    (C) Copyright IBM Corp. 2013, 2014
//
//    US Government Users Restricted Rights - Use, duplication or disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
//
//

(function() {
	// define namespace
	if (!window.com_ibm_cognos_cps){
		com_ibm_cognos_cps = {}
	}
	
	if (com_ibm_cognos_cps.ApplicationContext){
		// Stop if the ApplicationContext is already defined.
		return;
	}

	var _F_Array = 
	{
		indexOf: function(array, value, begin, strict)
		{
			for (var i = +begin || 0, l = array.length; i < l; i++) {
				if (array[i] === value || strict && array[i] == value) {
					return i;
				}
			}
			return -1;
		},

		unique: function (array, strict)
		{
			var a = [], i, l = array.length;
			for (i = 0; i < l; i++) {
				if (this.indexOf(a, array[i], 0, strict) < 0) {
					a.push(array[i]);
				}
			}
			return a;
		},

		forEach: function(array, func, thisArg)
		{
			if (typeof func == "function") {
				var i, v, l = array.length;		
				if (thisArg === undefined) {
					for (i = 0; i < l; i++) {
						func(array[i]);
					}
				} else {
					for (i = 0; i < l; i++) {
						func.call(thisArg, array[i]);
					}
				}
			}
		},
		
		insert: function(array, i, obj)
		{
			if (i !== undefined && obj !==undefined) {
				array.splice(i, 0, obj);
			}
		},

		remove: function(array, obj)
		{
			if (obj !== undefined)
			{
				var i = this.indexOf(array, obj);
				if (i >= 0)
				{
					array.splice(i, 1);
					return obj;
				}
			}
			return null;
		},

		removeAt: function(array, i)
		{
			if (i !== undefined) {
				array.splice(i, 1);
			}
		}
	};
	
	// Helper object used to create the xmlHttpRequest based on the current browser
	var CPSConfig = {
		browser: 'unknown',
		browserVersion: 'unknown',
		OS: 'unknown',
		xmlHttpDefault: null,
		emptyFunction:function(){},
		initialize: function()
		{
			this.browser = this.lookup(this.browsers).toLowerCase() || 'unknown';
			this.browserVersion = this.parseVersion(navigator.userAgent) || this.parseVersion(navigator.appVersion) || 'unknown';
			this.OS = this.lookup(this.systems) || 'unknown';	
			this.xmlHttpDefault = this.findXMLHttpActiveXVersion();		
		},	
		lookup: function(data)
		{
			var i, l = data.length;
			for (i = 0; i < l; i++)
			{
				this.versionKey = data[i].partialKey || data[i].identity;
				var agent = data[i].agent;
				if (agent)
				{
					if (agent.indexOf(data[i].key) != -1)
						return data[i].identity;
				}
				else if (data[i].prop)
					return data[i].identity;
			}
		},
		parseVersion: function(s)
		{
			var index = s.indexOf(this.versionKey);
			if (index == -1)
				return;
			return parseFloat(s.substring(index + this.versionKey.length + 1).replace(/[^\d\.\-\+]/g, '_'));	//sanitize before parse to avoid XSS vulnerability!
		},
		findXMLHttpActiveXVersion: function()
		{
			if (window.ActiveXObject) 
			{
				var i, l = this.xmlHttpVersions.length;
				for (i = 0; i < l; i++) 
				{
					try 
					{
						// Try and create the ActiveXObject for Internet Explorer, if it doesn't work, try again.
						var xmlhttp = new ActiveXObject(this.xmlHttpVersions[i]);
						if (xmlhttp) 
							return this.xmlHttpVersions[i];
					}
					catch (e) 
					{
						// this ActiveX is not there, continue with next one in list
				 	} 
				}
			}
			return null;
		},
		browsers: [
			{ agent: navigator.userAgent, key: 'MSIE',     identity: 'Explorer', partialKey: 'MSIE'     },
			{ agent: navigator.userAgent, key: 'Firefox',  identity: 'Firefox'                          },
			{ agent: navigator.userAgent, key: 'Gecko',    identity: 'Mozilla',  partialKey: 'rv'       },
			{ agent: navigator.userAgent, key: 'Mozilla',  identity: 'Netscape', partialKey: 'Mozilla'  },
			{ agent: navigator.userAgent, key: 'Netscape', identity: 'Netscape'                         },
			{ prop:  window.opera,                         identity: 'Opera'                            },
			{ agent: navigator.vendor,    key: 'Apple',    identity: 'Safari'                           }
		],

		systems: [
			{ agent: navigator.platform,  key: 'Win',      identity: 'Windows' },
			{ agent: navigator.platform,  key: 'Mac',      identity: 'Mac'     },
			{ agent: navigator.platform,  key: 'Linux',    identity: 'Linux'   }
		],

		xmlHttpVersions: [
			'Msxml2.XMLHTTP.6.0', 
			'Msxml2.XMLHTTP.3.0',
			'Msxml2.XMLHTTP',
			'Microsoft.XMLHTTP'
		]
		
	}

	CPSConfig.initialize();


	/*
		Helper DOM utils.
	*/
	com_ibm_cognos_cps._F_DOM =		
	{

		addEventListener: function (node, type, callback, capture)
		{
			type = type.toLowerCase();
			if (node.addEventListener) {
				node.addEventListener(type, callback, capture);
			} else if (node.attachEvent) {
				node.attachEvent('on' + type, callback);
			} else {
				node['on' + type] = callback;
			}
		},
		selectNodes: function(node, xpath)
		{
			if (document.all)
			{
				var arr = new Array();
				var nodes = node.selectNodes(xpath);
				var i, l = nodes.length;
				for (i=0; i<l; i++)
				{
					arr.push(nodes.item(i));
				}
				return arr;
			}
			else
			{
				var doc = (node.ownerDocument) ? node.ownerDocument : node;
				var results = doc.evaluate(xpath, node, doc.createNSResolver(doc.documentElement), XPathResult.ORDERED_NODE_ITERATOR_TYPE, null);
				if (results)
				{
					var arr = new Array();
					var node = results.iterateNext();
					while (node != null)
					{
						arr.push(node);
						node = results.iterateNext();
					}
					return arr;
				}
				return null;
			}
		},
		
		selectSingleNode: function (node, xpath)
		{
			if (document.all)
			{
				return node.selectSingleNode(xpath);
			}
			else
			{
				var doc = (node.ownerDocument) ? node.ownerDocument : node;
				var results = doc.evaluate(xpath, node, doc.createNSResolver(doc.documentElement), XPathResult.FIRST_ORDERED_NODE_TYPE, null);
				if (results && results.singleNodeValue)
					return results.singleNodeValue;
				return null;
			}
		},
		
		getAttribute: function(node, name)
		{
			return node.getAttribute(name);
		},
		
		text: function(node)
		{
			function deepScanText(node)
			{
				var v = '';
				var n = node.firstChild;
				while (n != null)
				{
					if (n.nodeType == 3)
						v += n.nodeValue;
					else if (n.nodeType == 4)
						v += n.data;
					else if (n.nodeType == 1)
						v += deepScanText(n);
					n = n.nextSibling;
				}
				return v;
			}

			if (node == null || node === undefined)
				return '';
			if (document.all)
				return node.text;
			else if (node.nodeValue)
				return node.nodeValue;
			return deepScanText(node);
		},
		
		parseXml: function(xmlString)
		{
			var xmlDoc = null;
			if (window.ActiveXObject){
				xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
				xmlDoc.async="false";
				xmlDoc.loadXML(xmlString);			
			}
			
			if (xmlDoc == null){
				var parser = new DOMParser();
				xmlDoc = parser.parseFromString(xmlString,"text/xml");
			}
			
			return xmlDoc;
		}

	}

	/*
		This class provides the APS that enables our content to work in enterprise portals.
			getWebContentResource: return an absolute url that will be used to retrieve a webcontent resource
			getGatewayResource: return an absolute url that will be used to retrieve a gateway resource
			getProxiedResource: return a url that can be used to do ajax request without running into cross domain issues
			getXHR: creates an xmlHttpRequest object.
		
	*/
	CPSWebApplication = com_ibm_cognos_cps.ApplicationContext = function(spec){
		if (spec){
			this.initialize(spec);
		}
		
	}
	
	
	//013 Create event handler map
	CPSWebApplication.eventHandler = {};
	
	CPSWebApplication.prototype = com_ibm_cognos_cps.ApplicationContext.prototype = {


		/*
		 * Sets the configuration object
		 */
		initialize: function(spec){
			
			if (this.initialized){
				return;
			}
			this.eventSubscriptions = {};
			this.properties = {}; //deprecated
			this.id = spec.namespacePrefix ? spec.namespacePrefix : '';
			this.userpreferences = spec.userPreferences;
			if (!this.userprefjson){
				this.userprefjson = {};
			}
			
			this.settings = spec.applicationSettings;
			if (!this.settings){
				this.settings = {};
			}
			
			this.cssmapping = spec.cssMapping;
			if (!this.cssmapping){
				this.cssmapping = {};
			}
			
			this.events = spec.events;
			if (!this.events){
				this.events = {listen:[], publish:[]};
			}
			
			this.navstate = spec.navigationState;
			if (!this.navstate){
				this.navstate = {};
			}
			
			this.navstatedirty = false; 
			this.webcontent = spec.webcontent;
			this.gateway = spec.gateway;
			
			this.getProperty = function(name){
				if (spec[name]){
					return spec[name];
				}else{
					return this.properties[name]; //deprecated
				}
			}
			
			
			if (!com_ibm_cognos_cps.ApplicationContext.instances){
				com_ibm_cognos_cps.ApplicationContext.instances = [];			
			}
			this.instanceIndex = com_ibm_cognos_cps.ApplicationContext.instances.length;
			com_ibm_cognos_cps.ApplicationContext.instances.push(this);
			
			
			// Check if we need to do a client login
			var features = this.getProperty('features');
			if (features && features.indexOf('BrowserLogin') != -1){
				this.login();
			}
			
			this.initialized = true;
		},
        /*
         * Return the portlet's unique ID, ex. "p134677430" 
         * This is prepended to all element ids in the portlet's html, and changes each page load
         */
		getID: function(){
		    return this.id;
		},
		
		/*
		 * Get the URI root for web content, ex. "http://exampleserver/cognos/"
		 */
		getWebContentURI:function(){
		    return this.webcontent;
		},
		setWebContentURI:function(value){
            this.webcontent=value;
        },
		
		/*
		 * get the URI for the gateway, ex. "http://exampleserver/cognos/cgi-bin/cognos.cgi"
		 */
		getGatewayURI:function(){
		    return this.gateway;
		},
		
		setGatewayURI:function(value){
		    this.gateway=value;
		},
		
		/*
		 *  Get the value of the named setting. 
		 *  Settings include things such as the portlet alias, and any settings the user can set from the portlet config page.
		 */
		getSetting:function(name){
			var value = this.settings[name];
			
			if (value === undefined){
				// The legacy sdk portlet appends 'sdk_' to the custom properties
				if (name.indexOf('sdk_') != 0) {
					name = 'sdk_' + name;
				}
		    	value=this.settings[name];
		    }
		    
		    if(value && (typeof value == 'object') && value.length != undefined){
		    	value = value[0];
		    }
		    
		    return value;
		},
		
		getMessage:function(resourceID) {
			var message = eval(resourceID);				
			return message;
		},

		getSettingValues:function(name){
			var value = this.settings[name];
			
			if (value === undefined){
				// The legacy sdk portlet appends 'sdk_' to the custom properties
				if (name.indexOf('sdk_') != 0) {
					name = 'sdk_' + name;
				}
		    	value=this.settings[name];
		    }
		    
		    
		    if(value && (typeof value == 'string')){
		    	var arr = [];
		    	arr.push(value);
		    	value = arr;
		    }
		    
		    return value;
		},
		
		/*
		 * get css mappings, used to change css classes
		 */
		getCssMapping:function(origialCss){
		    return this.cssmapping[origialCss];
		},
		
		/*
		 * get user preferences, which are set from the containing portal
		 */
		getUserPreference:function(name){
		    return this.userpreferences[name];
		},
		
        /*
         * Retrieve a value from the navigational state of the portlet.
         */
		getNavState:function(name){
		    return this.navstate[name];
		},
		
		/*
         * Add key=value type information to the navigational state of the portlet,
         * This allows state to be kept across page loads within the user session.
         * Both key and value must be string type data.
         * May cause delays when unloading the page, to write the changed state back
         */
		setNavState:function(name, value){
		    this.navstate[name] = value;
		    if (! this.navstatedirty){
		        this.navstatedirty = true;
		        document.body.onbeforeunload = this._hitch(this, this._writeNavState);
		    }
		},
		
		/*
		 * This removes the key=value pair from the navigational state of the portlet.
		 * Keys removed in this manner will no longer appear in the user session.
		 * May cause delays when unloading the page, to write the changed state back
		 */
		delNavState:function(name){
		    delete this.navstate[name];
		    if (! this.navstatedirty){
                this.navstatedirty = true;
                document.body.onbeforeunload = this._hitch(this, this._writeNavState);
            }
		},
		
		/*
		 * private helper function for setNavState() and delNavState() 
		 */
		_writeNavState:function(){
		    var query = [];
		    for (key in this.navstate) {
		        query.push(encodeURIComponent(key) + '=' + encodeURIComponent(this.navstate[key]));
		    }
		    var url = this._getActionURI(query.join('&'));
		    var xhr = this._getXHR();
		    xhr.open('GET', url, false);
		    xhr.send();		   
		},
		
		subscribe: function(name, func){
			this.eventSubscriptions[name] = func;
		},
		publish: function(name, data){
			var invoked = false;
			for (var i = 0 ; i < com_ibm_cognos_cps.ApplicationContext.instances.length; i++){
				var c = com_ibm_cognos_cps.ApplicationContext.instances[i];
				if (c != this && c.eventSubscriptions[name]){
					c.eventSubscriptions[name](data);
					invoked = true;
				}				
			}
			return invoked;
		},
		
		/*
		 *Adds an event handler to the handler map //013
		 */
		addListener: function(event, handler) {
			if (_F_Array.indexOf(this.events.listen, event) > -1) {
				if (CPSWebApplication.eventHandler[event]) {
					_F_Array.insert(CPSWebApplication.eventHandler[event], 0, handler);
				}
				else {
					CPSWebApplication.eventHandler[event] = new Array(handler);
				}
			}
			else {
				throw "This application does not listen for event: " + event;
			}
		},
		
		publishEvent: function(event, payload) {
			
			if(_F_Array.indexOf(this.events.publish, event) > -1) {
				
				var handlerArray = CPSWebApplication.eventHandler[event]
				
				if (handlerArray) {				
					for (var i = 0; i < handlerArray.length; i++) {
						var handler = handlerArray[i];
						
						if (handler) {
							handler(payload);
						}
					}
				}
				else {
					throw "No listeners present for event: " + event;
				}
			}
			else {
				throw "This application does not publish event: " + event;
			}
		},
		
        /*
         * makes an absolute url from a base and a url which maybe absolute or relative.
         */
		makeAbsolute: function(base, url){
			if(url.indexOf(base) !== 0){
				var index = base.indexOf('://');
				if (index !== -1){
					index = base.indexOf('/', index + 3);
					if (index !== -1){
						return base.substring(0, index) + (url.charAt(0) === '/' ? '' : '/' ) + url;
					}
				}
			}		
			return url;
		},
		
		/*
		 * Return a URI which will be proxied by the portal, so as to avoid cross site request issues
		 */
		getProxiedURI:function(url){
			var proxyTemplate = this.getProperty('proxyTemplate');		
			// no proxying in CC
			if (proxyTemplate && !('cognos' == this.getProperty('portalAgent'))){
				return this._cpsresourceurl(this.getProperty('proxyTemplate'), url);
			}
			return url;
		},
		
		/*
		 * Save the setting in the given form as portlet settings.
		 */
		saveSettings: function(form){
			var saveForm = document[this.getProperty('scope')+'_editSaveForm'];
			if (saveForm){
				for (var i = 0 ; i < form.elements.length; i++){
					if (form.elements[i].type != 'button'){				
						var input = document.createElement("input");
						input.name = "p_" + form.elements[i].name;
						input.type = form.elements[i].type;
						input.value = form.elements[i].value;
						input.checked = form.elements[i].checked;						
						saveForm.appendChild(input);
					} 
				}
				
				saveForm.submit();
			
			
			}
		},
		/*
		 * Reset the settings of a portlet.
		 */
		resetSettings: function(form){
			if (this.getProperty('editResetTemplate')) {
				document.location.href = this.getProperty('editResetTemplate');
			}
		},
		
		/*
		 * Cancel the editing of the setting and go back to view the app
		 */		
		cancelSettings: function(){
			if (this.getProperty('editCancelTemplate')) {
				document.location.href = this.getProperty('editCancelTemplate');
			}
		},
		
		
		getPortalAgent: function(){
			return this.getProperty('portalAgent');
		},
		
		/*
		 * Helper function that returns a url that will store the query to the nav state when accessed
		 */
		_getActionURI:function(query){
            var actionTemplate = this.getProperty('actionTemplate');  
            if (actionTemplate){
                return this._cpsactionurl(this.getProperty('actionTemplate'), query);
            }
            return null;
        },

        /*
         * This will log into the gateway, then call the callback. 
         * It may be called by many different portlets simultaneously 
         */
		login : function(callback){
			var passport = this.getProperty('camPassport');
			if (passport) {
	            if (CPSWebApplication.loadState == 'unloaded') {
	                CPSWebApplication.loadState = 'loading';
	                if (callback){
	                	CPSWebApplication.pendingCallbacks.push(callback);
	                }
	                
	                var div = document.createElement("div");
	                div.style.display='none';
	                var fr = document.createElement("iframe");
	                fr.src = this.gateway + "?b_action=xts.run&m=/cps4/common/sso.xts&m_passportID=" + encodeURIComponent(passport);
	                fr.onload = window.parent.CPSWebApplication.postLogin();
	                
	                div.appendChild(fr);
	                document.body.appendChild(div);
	                    
	            } else if (CPSWebApplication.loadState == 'loading' && callback) {
	                CPSWebApplication.pendingCallbacks.push(callback);
	                
	            } else if (CPSWebApplication.loadState == 'loaded' && callback) {
	                callback();	          
	            }
            } else if(callback){
            	callback();
            }
        },

		/*
		 * helper function for getProxiedURI(), 
		 */
		_cpsresourceurl: function(resourceTemplate, target){
			var begin = resourceTemplate.indexOf('_cpsresourceurl');
			if (begin != -1){
				var endMarker='cpsendmarker_';
				var end = resourceTemplate.indexOf(endMarker, begin); 
				if (end != -1){
					var marker = resourceTemplate.substring(begin, end + endMarker.length);
					var decodeCount = 0;
					var decodedMarker = marker;
					while(decodedMarker != '_cpsresourceurl:cpsendmarker_' && decodeCount ++ < 10){
						decodedMarker = decodeURIComponent(decodedMarker);
					}

					if (decodeCount  < 10){
						var encodedTarget = target;
						for (i =0; i < decodeCount ; i++){
							encodedTarget = encodeURIComponent(encodedTarget);
						}

						target = resourceTemplate.replace(marker, encodedTarget);
					}
				}
			}
			
			// Plumtree portletIds.. so we can synchronize passports.
			if (window.cognosPortletIds){
				if(target.indexOf('?') != -1)
					target += '&';
				else
					target += '?';				
				target += 'cognosPortletIds='+encodeURIComponent(cognosPortletIds.join('_'));
			}
			
			return target;	
		},
		
		/*
		 * helper function for _getActionURI()
		 */
		_cpsactionurl: function(resourceTemplate, target){
            var begin = resourceTemplate.indexOf('_cpsparamsurl');
            if (begin != -1){
                var endMarker='cpsendmarker_';
                var end = resourceTemplate.indexOf(endMarker, begin); 
                if (end != -1){
                    var marker = resourceTemplate.substring(begin, end + endMarker.length);
                    var decodeCount = 0;
                    var decodedMarker = marker;
                    while(decodedMarker != '_cpsparamsurl=cpsendmarker_' && decodeCount ++ < 10){
                        decodedMarker = decodeURIComponent(decodedMarker);
                    }

                    if (decodeCount  < 10){
                        var encodedTarget = target;
                        for (i =0; i < decodeCount ; i++){
                            encodedTarget = encodeURIComponent(encodedTarget);
                        }

                        target = resourceTemplate.replace(marker, encodedTarget);
                    }
                }
            }
            
            // Plumtree portletIds.. so we can synchronize passports.
            if (window.cognosPortletIds){
                if(target.indexOf('?') != -1)
                    target += '&';
                else
                    target += '?';              
                target += 'cognosPortletIds='+encodeURIComponent(cognosPortletIds.join('_'));
            }
            
            return target;  
        },
		
		/*
		 * returns an XHR request in a cross-browser fashion
		 */
		_getXHR: function(options){
		
			var obj;
            // plumtree xhr (knows how to handle timeouts ..)
            if (window.CustomPTHttpRequest !== undefined){
                obj = new CustomPTHttpRequest();
            }
                
            if (CPSConfig.xmlHttpDefault != null) {
                obj = new ActiveXObject(CPSConfig.xmlHttpDefault);
            }

            // Well if there is no ActiveXObject available it must be firefox, opera, or something else
            if (typeof XMLHttpRequest != 'undefined') 
            {
                obj =  new XMLHttpRequest();
            }
            if (!obj){
            	throw 'No XMLHttpRequest object is available';
            }
            
            if (options){
            
            
	            obj.onreadystatechange = function()
				{
					try
					{
						if (obj.readyState == 4)
						{
							if (obj.status >= 200 && obj.status < 300)
							{
								if (typeof options.onSuccess == "function") {
									options.onSuccess(obj);
								}
							}
							else
							{
								if (typeof options.onFailure == "function") {
									options.onFailure(obj);
								}
							}
			
							// fix for IE memory leak
							_self.transport.onreadystatechange = CPSConfig.emptyFunction;
						}
					}
					catch (e)
					{
						try
						{
							if (typeof options.onException == "function") {
								if (this.status !== 0) {
									options.onException(e);
								}
							}
						}
						catch (e1)
						{
							console.log('e1:' + e1);
						}
					}
				}; // of function
            }
            
            return obj;
        },
        
        /*
         * returns a function that will be called with the object as its scope
         */
        _hitch:function(obj, func){
            return function(){
                func.apply(obj);
            };
        },
		
		/*
		 * This can be called to load a library in such a way that it will only be loaded once on the page.
		 */
		loadLibrary: function(href, callback, conditionFunction)
		{		
			if (conditionFunction && !conditionFunction()){				
				if (callback){
					callback(false);
				}
				return false;
				
			}
			
			
			if (!window.CognosCPSLoadedLibMap){ 
				window.CognosCPSLoadedLibMap = {};				
			}
			
			
			
			if ( !window.CognosCPSLoadedLibMap[href])
			{
				CognosCPSLoadedLibMap[href] = true;
				
				var callbacks = [];
				CPSWebApplication.loadingCallbacks[href] = callbacks;
				callbacks.push(callback);
								
				var head = document.getElementsByTagName('HEAD').item(0);
				var node = document.createElement('SCRIPT');
				var _self = this;		
		
				node.onload = node.onreadystatechange = function()
				{
					if (this.readyState && this.readyState != 'loaded' && this.readyState != 'complete') {
						return;
					}
				
					var callbacks = CPSWebApplication.loadingCallbacks[href];
					for (var i = 0 ; i < callbacks.length; i++){
						callbacks[i](true);
					}
				
					delete CPSWebApplication.loadingCallbacks[href];
				};
				node.type = 'text/javascript';
				node.src = href;
				head.appendChild(node);
				return true;
			}
			
			if (callback){


				var callbacks = CPSWebApplication.loadingCallbacks[href];
				if (callbacks){
					callbacks.push(callback);
				}else {
					callback();	
				}
			}
			
			return false;
		}

	}
    
    /*
     * helper function for login()
     */
    CPSWebApplication.postLogin = function() {
        CPSWebApplication.loadState = 'loaded';
        for (var i = 0; i < CPSWebApplication.pendingCallbacks.length; i++) {
            CPSWebApplication.pendingCallbacks[i]();
        }
    }
    CPSWebApplication.loadState = 'unloaded';
    CPSWebApplication.pendingCallbacks = [];
    
    CPSWebApplication.loadingCallbacks = {};
	
})();

window.CognosConnect = new CPSWebApplication();