/* 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
};