/* Copyright (c) 2011, Geert Bergman (geert@scrivo.nl)
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 * 3. Neither the name of "Scrivo" nor the names of its contributors may be
 *    used to endorse or promote products derived from this software without
 *    specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * $Id: SUI.js 750 2013-07-23 01:48:11Z geert $
 */

"use strict";

/**
 * @summary
 * Root namespace of the <u>S</u>crivo <u>U</u>ser <u>I</u>nterface library.
 *
 * @description
 * The SUI namespace is the root namespace of the <u>S</u>crivo <u>U</u>ser
 * <u>I</u>nterface library. It holds all the classes of the basic UI
 * components as well as some general utility functions. More specialized
 * classes or sets of utility functions will have their own namespace
 * within the SUI namespace.
 * 
 * @namespace
 */
var SUI = {

	/**
	 * @summary
	 * The SUI.control namespace holds a set of more advanced user interface 
	 * controls.
	 * 
	 * @description
	 * The SUI.control namespace is the home for more advanced controls.
	 * These controls are specialized components such as a date entry
	 * component, an image cropper or an HTML editor. All are based on
	 * SUI.Box/SUI.AnchorLayout so they all can be used in the SUI lay out
	 * mechanism.
	 * 
	 * @namespace
	 */
	control: {},

	/**
	 * @summary
	 * The SUI.dialog namspace holds a set of common dialog boxes.
	 * 
	 * @description
	 * The SUI.dialog namespaces contains a set of common dialog boxes. All are
	 * based on the SUI.Window class. You'll find here (asynchronious)
	 * substitutes for JavaScript's alert, confirm and prompt popup boxes.
	 * There's also the typical calendar dialog and an empty dialog with an
	 * OK and cancel button that you can use as a base for your own dialogs.
	 * 
	 * @namespace
	 */
	dialog: {},

	/**
	 * @summary
	 * The SUI.form namspace contains a set of wrappers for the typical HTML 
	 * form components.
	 * 
	 * @description
	 * The SUI.form namespace holds a set of very lightweight wrappers around
	 * the standard HTML form controls. We like the standard HTML form
	 * elements and we want to use them as such. These wrappers add
	 * functionality to the HTML form elements so that they can be used in
	 * SUI layouts.
	 * 
	 * @namespace
	 */
	form: {},

	/**
	 * Location of the images relative to the SUI source directory. Change
	 * this value if you want to use an external image library.
	 * @type String
	 */
	imgDir: "",

	/**
	 * @summary
	 * Utility function to facilitate JavaScript's prototypal inheritance
	 * mechanisms.
	 * 
	 * @description
	 * <p>Utility function to facilitate JavaScript's prototypal inheritance
	 * mechanisms. The aim is to stay close to the language standard but
	 * to sand off some of the rough edges and to create a standard pattern
	 * to use within the Scrivo UI library.
	 * </p>
	 * <p>Example (boiler plate code):
	 * </p>
	 * <pre class="prettyprint">
	 * MyClass = SUI.defineClass({
	 *     initializer: function(arg) {
	 *         // Your constructor code here
	 *     },
	 *     // your prototype members and methods here:
	 *     sampleMethod: function(a) {
	 *         return "[" + a + "]";
	 *     }
	 * });
	 *
	 * MySubClass = SUI.defineClass({
	 *     baseClass: MyClass, // your base class
	 *     initializer: function(arg) {
	 *         MySubClass.initializeBase(this, arg); // initialize base class
	 *         // Your constructor code here
	 *     },
	 *     // your prototype members and methods here:
	 *     sampleMethod: function(a) { // override a method
	 *         return "> " + MySubClass.parentMethod(this, "sampleMethod", a);
	 *     }
	 * });
	 * </pre>
	 *
	 * <p>Or more prosaic:</p>
	 *
	 * <pre class="prettyprint">
	 * Person = SUI.defineClass({
	 *     initializer: function(arg) {
	 *         if (arg.name) {
	 *             this.name = arg.name;
	 *         }
	 *     },
	 *     name: ""
	 * });
	 *
	 * Employee = SUI.defineClass({
	 *     baseClass: Person,
	 *     initializer: function(arg) {
	 *         Employee.initializeBase(this, arg);
	 *     },
	 *     salary: 20000,
	 *     raise: function(percentage) {
	 *         return this.salary * (1 + percentage/(100 * 10));
	 *     }
	 * });
	 *
	 * Manager = SUI.defineClass({
	 *     baseClass: Employee,
	 *     initializer: function(arg) {
	 *         Manager.initializeBase(this, arg);
	 *     },
	 *     salary: 40000,
	 *     raise: function(percentage) {
	 *         return 3 * Manager.parentMethod(this, "raise", percentage);
	 *     }
	 * });
	 * </pre>
	 * @param {Object} arg An object containing the class definition.
	 */
	defineClass: function(arg) {

		// If we're creating a base class
		if (arg.baseClass) {
			// Commonly you'll see something like:
			//   arg.initializer.prototype = new arg.baseClass();
			// But the little trick below will prevent that the base class
			// constructor is executed during this stage of class definition.
			var tmp = function() {};
			tmp.prototype = arg.baseClass.prototype;
			arg.initializer.prototype = new tmp();

			// Now reset the prototype constructor back to the original
			arg.initializer.prototype.constructor = arg.initializer;
			// And keep a copy of the base class
			arg.initializer.base = arg.baseClass;
		}

		// Add members and methods to the prototype of the class
		for (var i in arg) {
			// initializer and baseClass are not part of the prototype body
			if (i != "initializer" && i != "baseClass") {
				arg.initializer.prototype[i] = arg[i];
			}
		}

		/**
		 * Each class gets a static method 'initializeBase' which is a
		 * convenience method to call the initializer of the base class.
		 *
		 * @param that: the initializer's this reference
		 * @param -: additional arguments
		 */
		arg.initializer.initializeBase = function(that) {
			// call the base class constructor
			this.base.prototype.constructor.apply(
				that, [].slice.call(arguments, 1));
		};

		/**
		 * Each class gets a static method 'parentMethod' which is a
		 * convenience method to call overridden methods of the base class.
		 *
		 * @param that: the objects this reference
		 * @param method: the overridden method name
		 * @param -: additional arguments
		 */
		arg.initializer.parentMethod = function(that, method) {
			// call the base class method
			return this.base.prototype[method].apply(
				that, [].slice.call(arguments, 2));
		};

		// Now return our class
		return arg.initializer;
	},

	/**
	 * @summary
	 * Initialize the SUI library.
	 * 
	 * @description
	 * <p>Initialize the SUI library. Some initial values and event handlers
	 * need to be set to ensure that all parts of the SUI library will work
	 * correctly. The SUI library was designed to have a minimum footprint so
	 * here it is what is does on initialization:</p>
	 * <ul>
	 * <li>Get the browser type,</li>
	 * <li>Two IE patches (Array.indexOf and String.substr),</li>
	 * <li>Get the viewport size and add an window.onresize handler to keep
	 *     these values current,</li>
	 * <li>Notify the SUI.onStart event listener.</li>
	 * </ul>
	 * <p>That was all folks, no lengthy initialization stuff for us!</p>
	 */
	initialize: function() {

		if (this._initialized) {
			return;
		}
		this._initialized = true;

		// get the browser version
		SUI.browser.getBrowser();

		// some older browsers (IE) don't have an indexOf for array's
		SUI.browser.patchNoArrayIndexOf();
		// IE interpretates a negative index for substr different than others
		SUI.browser.patchSubstrIE();

		// get the viewport (browser window) size ...
		SUI.browser.getVieportSize();
		// ... and keep them current
		SUI.browser.addEventListener(window, "resize", function() {
			SUI.browser.getVieportSize();
		});

		// notify the onStart listeners as soon as the DOM tree is ready
		if (document.addEventListener) {
			document.addEventListener("DOMContentLoaded", function(){
				SUI.onStart();
			}, false);
		} else {
			// need to wait a little longer for IE
			window.onload = function() {
				SUI.onStart();
			};
		}

		// get the location of the images
		var loc = document.location.href;
		var n = loc.indexOf("/sui/");
		if (n !== -1) {
			this.imgDir = loc.substr(0, n+5) + "img";
		} else {
			this.imgDir = "img";
		}

	},

	/**
	 * @summary
	 * Left trim a string.
	 * 
	 * @description
	 * Left trim a string. Remove the leading whitespace (space, non breaking
	 * space, tab, line feed, carriage return) from a string.
	 * 
	 * @param {String} s The string to trim.
	 * @return {String} The trimmed string.
	 */
	ltrim: function(s) {
		return s.replace(/^[\s\xA0\t\r\n]+/,"");
	},

	/**
	 * @summary
	 * Right trim a string.
	 * 
	 * @description
	 * Right trim a string. Remove the trailing whitespace (space, non breaking
	 * space, tab, line feed, carriage return) from a string.
	 * 
	 * @param {String} s The string to trim.
	 * @return {String} The trimmed string.
	 */
	rtrim: function(s) {
		return s.replace(/[\s\xA0\t\r\n]+$/,"");
	},

	/**
	 * @summary
	 * Trim a string.
	 * 
	 * @description
	 * Trim a string. Remove the leading and trailing whitespace (space, non
	 * breaking space, tab, line feed, carriage return) from a string.
	 * 
	 * @param {String} s The string to trim.
	 * @return {String} The trimmed string.
	 */
	trim: function(s){
		return this.ltrim(this.rtrim(s));
	},

	/**
	 * The onStart event handler, is called when all necessary initialization
	 * actions are done and the DOM tree is ready to be manipulated.
	 * @event
	 */
	onStart: function() {},

	/**
	 * Flag so we won't reinitialize SUI again.
	 * @private
	 */
	_initialized: false

};