/**
 * Shell component
 *
 * The Shell is a pseudo-controller for the application front-end, and acts as a proxy for all AJAX requests and updates.
 * @author Chris Blunt
 */

// Additional methods for DOM elements
Element.addMethods({

    /**
     * Insert a SWFObject video player into the current element.
     *
     * @param {Element} element The element into which the player will be inserted
     * @param {string} domain The current website domain
     * @param {integer} resource_id The resource id of the video to play
     */
    insertSWFVideoPlayer: function(element, domain, resource_id)
    {
        var element = $(element);
        var height_multiplier = 400 / 620; // Default width/height of the movie player swf

        var resource_url = domain + '/resources/view/id/' + resource_id + '.flv';
        var flash_vars = {
            file: resource_url
        };
        var params = {
            WMode: 'transparent'
        };

        swfobject.embedSWF(
            '/app/common/assets/flash/flvplayer.swf',
            element.id,
            element.getWidth(),
            element.getWidth() * height_multiplier,
            "7.0.0",
            false,
            flash_vars,
            params
        );
    },

    /**
     * Centre an element to the current viewport
     *
     */
    centre: function(element) {
        try{
            element = $(element);
        }catch(e){
            return;
        }

        var my_width  = 0;
        var my_height = 0;

        if ( typeof( window.innerWidth ) == 'number' ){
            my_width  = window.innerWidth;
            my_height = window.innerHeight;
        }else if ( document.documentElement &&
                 ( document.documentElement.clientWidth ||
                   document.documentElement.clientHeight ) ){
            my_width  = document.documentElement.clientWidth;
            my_height = document.documentElement.clientHeight;
        }
        else if ( document.body &&
                ( document.body.clientWidth || document.body.clientHeight ) ){
            my_width  = document.body.clientWidth;
            my_height = document.body.clientHeight;
        }

        element.style.position = 'absolute';
        element.style.zIndex   = 99;

        var scrollY = 0;

        if ( document.documentElement && document.documentElement.scrollTop ){
            scrollY = document.documentElement.scrollTop;
        }else if ( document.body && document.body.scrollTop ){
            scrollY = document.body.scrollTop;
        }else if ( window.pageYOffset ){
            scrollY = window.pageYOffset;
        }else if ( window.scrollY ){
            scrollY = window.scrollY;
        }

        var elementDimensions = Element.getDimensions(element);

        var setX = ( my_width  - elementDimensions.width  ) / 2;
        var setY = ( my_height - elementDimensions.height ) / 2 + scrollY;

        setX = ( setX < 0 ) ? 0 : setX;
        setY = ( setY < 0 ) ? 0 : setY;

        element.style.left = setX + "px";
        element.style.top  = setY + "px";

        return element;
    }
});

// # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 
// Window/Document-level Event Observers

document.observe('dom:loaded', function() {
	
	if(AS4Shell.getInstance().authenticated==true){
		AS4Shell.getInstance().isAuthenticated();
	}
	
	// Check for the dirt that is IE6
	Prototype.Browser.IE6 = (parseInt(navigator.userAgent.substring(navigator.userAgent.indexOf("MSIE")+5))==6);

	// Update PNG image tags for IE6
	AS4Shell.getInstance().renderTransparencies();

	// Hide any notification elements
	$('master_ajax_indicator').setStyle({
		opacity: 0.75
	});
	
	$$('.ajax_status').invoke('hide');
		
	// Observe window resize event and bind the resize event
	AS4Shell.getInstance().onResizeWindow(); 
	Event.observe(window, 'resize', AS4Shell.getInstance().onResizeWindow.bindAsEventListener(this));

	if(AS4Shell.getInstance().willPrint)
		window.print();
},null);

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

// Observe a document resize
Event.observe(window, 'resize', function() {
	AS4Shell.getInstance().calculateBrowserViewportDimensions();
});

// # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 
// Class Declaration

var AS4Shell = Class.create({

	/**
	 * @type {Object} The width and height of the browser display
	 */ 
	browserViewportSize: null,

	/**
	 * @type {boolean} Indicate if a modal lightbox is currently displayed
	 */
	lightboxActive: false,

	/**
	 * @type {Lightbox} The shell's lightbox
	 */
	lightbox: null,

	/**
	 * @type {bool}
	 */
	authenticated: false,

	/**
	 * @type {string} The current context open domain
	 */
	openDomain: null,
	
	/**
	 * @type {string} The current context secure domain
	 */
	secureDomain: null,

	/**
	 * @type {integer} The count of currently active XMLHttpRequests. A count is kept so that one AJAX request finishing does not switch off the global 'ajax_status' indicators when there are other AJAX requests still awaiting a response.
	 */
	requestCount: 0,
	
	/**
	* @type (boolean ) Whether or not to show the file uploader
	**/
	fileUpload: true,

	/**
	 * @type {boolean} Indicates if the loading page will be printed
	 */
	willPrint: false,

	FLASH_OBJECT_ID: 'FileUpload',
	currentComponent: 'resource_manager',
	currentChannel: 0,
	// # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 

	initialize: function()
	{
		this.calculateBrowserViewportDimensions();
	},
	
	/**
	 * Reload the current page
	 */
	reload: function()
	{
		window.location.reload();
	},

	/**
	 * Redirect to the given url
	 */
	redirect: function(url)
	{
		top.location=url;
	},

	/**
	 * Parses the current document and re-renders all PNGs as transparents in IE
	 * 
	 * @param {Element} Optional element in which the transparencies will be rendered. Default is null (all images in the document).
	 * @return {void}
	 */
	renderTransparencies: function(element)
	{
		if(!Prototype.Browser.IE6)
			return;
	
		var filter = 'DXImageTransform.Microsoft.AlphaImageLoader';
		if(typeof(element) != 'undefined' && $(element))
			var images = $(element).select('img');
		else
			var images = $$('img');

		images.each(function(img) {
			var src = img.src;
			
			// Only apply when the image is a PNG
			if (src.indexOf('.png') != -1) 
			{	
				$(img).setStyle({
					filter: "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + src + "',sizingMethod='scale')",
					width: img.width > 0 ? img.width : 16,
					height: img.height > 0 ? img.height : 16
				});

				// We need to store the original img.src somewhere in case this image is to become a super draggable, so for
				// now stick the src in the img tag's rel attribute.
				img.rel = src;
				img.src = '/app/common/assets/images/blank.gif';
			}
		});			
	},


	// # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #    
	// Super Draggables for elements contained within overflow:scroll divs

	/**
	 * Make an element a super-draggable, that is a node that when clicked is cloned to the top-document level
	 * and the clone then made draggable
	 *
	 * NOTE: This function isn't foolproof with IE8's new caret-browsing mode. If the caret mode is active, the screen
	 * can suddenly jump around when creating a new element in the DOM. Just something watch out for...
	 * 
	 * @param {Object} event
	 */
	makeSuperDraggable: function(event, element)
	{
		if(!event.isLeftClick())
			return;	

		if(Prototype.Browser.IE6)
			var source = $(element).down('img').readAttribute('rel');
		else
			var source = $(element).down('img').src;

		// If not a PNG and in IE6, the rel tag will be null. Doh! 
		if(Prototype.Browser.IE6 && source == null)
			source = $(element).down('img').src;
	
		this.draggableClone = Builder.node('img', {"src": source, "class": 'draggable'});
		this.draggableClone = $(this.draggableClone);

		$w($(element).className).each(function(name){
			$(this.draggableClone).addClassName(name);
		}.bind(this));

		$(this.draggableClone).id = $(element).up('div').id + "_clone";

		$('page_wrapper').insert(this.draggableClone);

		$(this.draggableClone).absolutize();
		$(this.draggableClone).clonePosition(element);

		var draggable = new Draggable(this.draggableClone, {
			revert: false,
			starteffect: null,
			endeffect: null,
			reverteffect: null,
			onEnd: function(event) {
				if($(this.draggableClone).parentNode)
					$(this.draggableClone).remove();	
			}.bind(this)
			
		});
	
		draggable.initDrag(event);
	},


	// # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #    
	// File upload form methods
  //
  //
	insertFileUploadForm: function(targetID, currentChannel, options)	
	{
		window["FileUpload"] = new Object();

    var options = Object.extend({
      after: function (e) {
        $('flash_form').submit()
    }}, options || {});

		var so = new SWFObject("/app/common/assets/fileUploader/FileUpload.swf", "FileUpload", "600", "600", "8", "#ffffff");
		so.addParam("quality", "high");
		so.addParam("wmode", "transparent");
		so.addParam("name", "FileUpload");
		this.currentChannel = currentChannel;
		var session = $('session').value;
			
		so.addVariable("FVuploadURL", "/resource_editor/upload_files/channelId/"+currentChannel+"?session_token=" + session);
		//so.addVariable("FVuploadURL", "https://secure.activesite4-ms.wbdev.webbasedltd.local:11448/secure/upload_files/channelId/"+currentChannel+"?session_token=" + session);
		so.addVariable("cdp", "/app/common/assets/fileUploader/crossdomainpolicy.xml");
		so.addVariable("xmlUrl", "/app/common/assets/fileUploader/FileUploadForm.xml");
	
		so.write("flashUploaderForm");
	
		if (navigator.appVersion.indexOf("MSIE") != -1)
		{
			window["FileUpload"] = new Object();
			this.SWFFormFixAuto();
		}	

    window["FileUpload"].after = options.after;
    this.getUploader().after = options.after;

	},	

	// # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #    
	// Secure File upload form methods
  //
  //
	insertSecureFileUploadForm: function(targetID, currentChannel, options, url)	
	{
		window["FileUpload"] = new Object();

    var options = Object.extend({
      after: function (e) {
        $('flash_form').submit()
    }}, options || {});

		var so = new SWFObject("/app/common/assets/fileUploader/FileUpload.swf", "FileUpload", "600", "600", "8", "#ffffff");
		so.addParam("quality", "high");
		so.addParam("wmode", "transparent");
		so.addParam("name", "FileUpload");
		this.currentChannel = currentChannel;
		var session = $('session').value;
			
		//so.addVariable("FVuploadURL", "/resource_editor/upload_files/channelId/"+currentChannel+"?session_token=" + session);
		so.addVariable("FVuploadURL", "/secure/upload_files/channelId/"+currentChannel+"?session_token=" + session+ "&secure=true");
		so.addVariable("cdp", "/app/common/assets/fileUploader/crossdomainpolicy.xml");
		so.addVariable("xmlUrl", "/app/common/assets/fileUploader/FileUploadForm.xml");
	
		so.write("flashUploaderForm");
	
		if (navigator.appVersion.indexOf("MSIE") != -1)
		{
			window["FileUpload"] = new Object();
			this.SWFFormFixAuto();
		}	

    window["FileUpload"].after = options.after;
    this.getUploader().after = options.after;

	},	

	SWFFormFixAuto: function() 
	{
		if (navigator.appName.toLowerCase() != "microsoft internet explorer")
			return true;
	
		window["FileUpload"] = new Object();
	
		var objects = document.getElementsByTagName("object");
		if (objects.length == 0)
			return true;
		for(i=0; i<objects.length; i++) 
		{
			// here's all the objects on the page, now lets find the flash objects
			if (objects[i].classid == "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000") {
				// this is a flash movie, apply the fix
				window[objects[i].id] = objects[i];
			}
		}
	
		return true;
	},
		
	uploadError: function(errMsg) {
		//dbg('Upload error', errMsg);
		alert(errMsg);
	},
	uploadProgress: function() {
		//dbg('Progress ',arguments);
	},
	uploadProgressIndividual: function() {
		//dbg('Progress Individual ',arguments);
	},
	uploadSelectedFiles: function(arrFileSizes) {
		//console.log('uploadSelectedFiles ',arguments);
		
		var limit = (100 * 1024 * 1024);
		var error = false;
		
		$A(arrFileSizes).each(function(size) {
			//alert("Size: "+size+"\nLimit: "+limit);
			if(size > limit)
			{
				error = true;
			}
		});

		if(error === true)
		{
			$('uploadFile').href = "javascript:alert('You cannot upload a file larger than 100 MB.');";
		}
		else if(error === false)
		{
			$('uploadFile').href = "javascript:AS4Shell.getInstance().saveAddFiles();";
		}
		
	},
	uploadComplete: function(totCompleted, totFailed) {
		if(totFailed == 0)
      this.getUploader().after();
	},	

	getUploader: function() {
		var id = this.FLASH_OBJECT_ID;
		if (document[id])
			return document[id];
		if (!window[id])
			window[id] = $(id);
			
		return window[id];
	},	

	saveAddFiles: function(sessionId) {
		//console.log('saveAddFiles ',arguments);

				
		var domain = "";
		
		if(this.secureDomain)
		{
			domain = this.secureDomain;
			var secure = true;
		}
		else
		{
			domain = this.openDomain;
		}
		
		var session = $('session').value;
		//var flashUrl = this.openDomain + '/resource_editor/upload_files/channelId/' + this.currentChannel + '?session_token=' + session;
		if(secure === true)
		{
			var flashUrl = domain + '/resource_editor/secure_upload_files/channelId/' + this.currentChannel + '?session_token=' + session + '&secure=true';
		}
		else
		{
			var flashUrl = domain + '/resource_editor/upload_files/channelId/' + this.currentChannel + '?session_token=' + session ;
		}
	
		// NB. This will not be reliable if there can be more than one FileUpload
		// control visible on a single page.
		
		// Trigger Flash uploader.		
		this.getUploader().startFileUpload(flashUrl);
	},	

	saveAddSecureFiles: function(sessionId, secureUrl) {
		//console.log('saveAddFiles ',arguments);

		var session = $('session').value;
	//	var flashUrl = this.openDomain + '/resource_editor/upload_files/channelId/' + this.currentChannel + '?session_token=' + session;
		var flashUrl = secureUrl+'/secure/upload_files/channelId/' + this.currentChannel + '?session_token=' + session;
		// NB. This will not be reliable if there can be more than one FileUpload
		// control visible on a single page.
		
		// Trigger Flash uploader.		
		this.getUploader().startFileUpload(flashUrl);
	},	

	checkFileSizes: function(sessionId) {
		var session = $('session').value;
		//var flashUrl = this.openDomain + '/resource_editor/upload_files/channelId/' + this.currentChannel + '?session_token=' + session;
		var flashUrl = 'https://secure.activesite4-ms.wbdev.webbasedltd.local:11448/secure/upload_files/channelId/' + this.currentChannel + '?session_token=' + session;
		// NB. This will not be reliable if there can be more than one FileUpload
		// control visible on a single page.
		this.uploadComplete = this.uploadComplete.bind(this);
		
		// Trigger Flash uploader
		this.getUploader().startFileUpload(flashUrl);
	},	

	// # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #    
	// Lightbox Helper Methods
	
	/**
	 * Display a modal lightbox.
	 * 
	 * Params are as:
	 * <pre>
	 * {
	 *   url: {string}
	 * }
	 * </pre>
	 * 
	 * @param {Object} params
	 */
	showLightbox: function(params)
	{
		// Start the lightbox and pass in the URL we want to load into it.
		LightboxHelper.showCallback(params.callback);

		var defaults = {
			width: 400,
			height: 300	
		};

		params = Object.extend(defaults, params);

		$('lightbox').setStyle({
			width: params.width  + 'px',
			height: params.height + 'px',
      marginLeft: -(params.width / 2) + 'px',
      marginTop: -(params.height / 2) + 'px',
			overflow: 'hidden'
		});
		
		this.lightboxActive = true;
	},
	
	closeLightbox: function()
	{
		if(!this.lightboxActive)
			return;

		LightboxHelper.close();
		this.lightboxActive = false;
	},
	
	// # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #    
	// Interface helper methods
	
	/**
	 * Toggle the given elements' visibility.
	 * 
	 * @param {Array} elements Array of element ids that will be toggled
	 * @param {string} [effect] Optional toggle effect to apply. Default is 'slide'.
	 */
	toggle: function(elements, effect)
	{
		var effect = (effect == null ? 'slide' : effect);
		 
		$A(elements).each(function(element){
			//$(element).toggle();
			Effect.toggle(element, effect);
		});
	},
	
	
	
	// # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #    
	// AJAX Methods
	
	/**
	 * Update the given DOM element with the parsed XHTML. Options is an optional
	 * object containing extra configuration information:
	 *
	 * <pre>
	 * {
	 *     message: {string} The message to display in the ajax status panel(s). Default is 'Loading...'
	 *     method: [{string}] Optional method for the AJAX request. Default is 'get'
	 *     showIndicator: [{boolean}] Optional boolean to show the status indicator for this request. Default is true.
	 *     updateCondition: [{function}] Optional method indicating whether the content should be updated. This method must return a boolan value. Default is return TRUE. 
	 * }
	 * </pre>
	 *
	 * @param {Element} target The containing element to update
	 * @param {Array} [callbacks] Optional array of callback methods to trigger on success.
	 * @param {Object} [options] Object of options as above. 
	 * @param {boolean} [showIndicator] Optional boolean to show the status indicator. Default is true.
	 */
	ajaxUpdate: function(url, params, target, callbacks, options)
	{
		var defaults = {
			message: 'Loading...',
			method: 'get',
			showIndicator: true,
			updateCondition: function(transport, target) {return true;}
		};	
		
		options = Object.extend(defaults, options);
		
		if(options.showIndicator)
			$$('#master_ajax_indicator.ajax_status .ajax_message').each(function(element){element.update(options.message)});

		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
		this.onAjaxBegin(options.showIndicator);

		new Ajax.Request(url, {
			method: options.method,
			parameters: params,
			onSuccess: function(transport){
				// Call the generic callback
				this.ajaxCallback(transport, target, options);
				// Iterate each registered callback
				if(callbacks)
				{
					callbacks.each(function(method) {
						method(transport, target);
					})
				}
			}.bind(this)
		});
	},

	isAuthenticated: function()
	{
		if(AS4Shell.getInstance().authenticated==true){
			var url = '/secure/is_authenticated';
			var params = {
				renderMode: 'update'
			}
			this.ajaxUpdate(url, $H(params).toQueryString(), 'is_authenticated', $A([this.onLoadIsAuthenticated]),{showIndicator: false});
		}
	},	
		
	/**
	 * Event triggered when the onLoadIsAuthenticated returns content
	 */
	onLoadIsAuthenticated: function(transport, target)
	{
		if(transport.headerJSON && transport.headerJSON.exception){
			return;
		}
		if(transport.headerJSON.meta)
		{			
			if(transport.headerJSON.meta.is_authenticated){
				setTimeout(function(){AS4Shell.getInstance().isAuthenticated();},300000);
				
			}
			else{
				clearTimeout();
				AS4Login.getInstance().showLoginLightbox();
			}
		}					
	},			
	
	/**
	 * Generic AJAX request callback method. Forwards to any other registered callback
	 * 
	 * @param Object transport The returned AJAX.Response
	 * @param Element target The target element container to be replaced by the returned content
	 * @param {Hash} [options] Optional hash of options that were passed in the initial request
	 */
	ajaxCallback: function(transport, target, options)
	{
		var shouldUpdate = options.updateCondition ? options.updateCondition(transport, target) : true;
		
		// First ensure we have not been sent a JSON-encoded AJAX exception
		if(transport.headerJSON && transport.headerJSON.exception)
		{
			this.onAjaxFinish();
			this.handleAJAXException(transport.headerJSON);
			return;
		}
		
		// Check if the flash has any messages
		if(transport.headerJSON && transport.headerJSON.flash && transport.headerJSON.flash.message)
			alert(transport.headerJSON.flash.message);
		
		// Update the target
		if(shouldUpdate && target && transport.responseText && transport.responseText.length != 0)
		{
			try
			{
				$(target).replace(transport.responseText);
					
				// Re-apply PNG transparencies in the dirt that is IE6
				this.renderTransparencies();
			}
			catch(e)
			{}
		}

		this.onAjaxFinish();
	},
	
	/**
	 * Generic exception handler for an AJAX request that went bad!
	 *
	 * @param {Object} json The evaluated JSON object
	 */
	handleAJAXException: function(json)
	{
		if(json.alert)
			alert(json.message);
	},
	
	/**
	 * Notification received when an AJAX request beings
	 * @param {boolean} [showIndicator] Optional boolean to show the status indicator. Default is true.
	 */
	onAjaxBegin: function(showIndicator)
	{
		var showIndicator = (showIndicator == null ? true : showIndicator);
		this.requestCount++;
		
		if (showIndicator) 
		{
			$$('.ajax_status').each(function(element){
				$(element).show();
			}.bind(this));
		}	
	},
	
	/**
	 * Notification received when an AJAX request has ended
	 */
	onAjaxFinish: function(transport, target, didShowIndicator)
	{
		this.requestCount--;

		if(this.requestCount <= 0) 
		{
			$$('.ajax_status').invoke('hide');
			this.requestCount = 0;
		}
	},

	// # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #    
	// Window-Level Event Methods
	
	onResizeWindow: function(event)
	{
		$$('div.centred').each(function(element){
			Element.centre($(element));
		});
	},
	

	// # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
	// Protected methods
	
	/**
	 * Calculate the browser's viewport dimensions and store in the AS4Shell's properties
	 */
	calculateBrowserViewportDimensions: function()
	{

		// Calculate the browser viewport dimensions.
		// NB. In order to take into account the appearance of scrollbars we need
		// to use the clientWidth/Height properties (innerWidth/Height ignore scrollbars).
		if( document.documentElement && ( document.documentElement.clientWidth || document.documentElement.clientHeight ) ) {

			// FF & IE 6+ in 'standards compliant mode'
			var browserViewportWidth = document.documentElement.clientWidth;
			var browserViewportHeight = document.documentElement.clientHeight;

		} else if( document.body && ( document.body.clientWidth || document.body.clientHeight ) ) {

			// IE 4 compatible
			var browserViewportWidth = document.body.clientWidth;
			var browserViewportHeight = document.body.clientHeight;

		}

		// Store the browser viewport dimensions.
		this.browserViewportSize = {
			width: browserViewportWidth,
			height: browserViewportHeight 
		};
	}
})

// # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 
// Static Methods
// Static instance of the AS4Shell object
AS4Shell.instance = null;

/**
 * Return a singleton instance of the AS4Shell object
 * @return AS4Shell
 */
AS4Shell.getInstance = function()
{
	if(!AS4Shell.instance)
		AS4Shell.instance = new AS4Shell();
		
	return AS4Shell.instance;
}

/**
 * Wrap the console debug method
 * @param {Object} object
 */
AS4Shell.log = function(object)
{
	if(typeof(console) == 'undefined')
		return;
		
	console.log(object);
}
