/* -------------------------------------------------------------------------- */
/** 
 *    @fileoverview
 *       CWJ JavaScript Library base script.
 *
 *    @version 1.0.20100701
 *    @requires jquery.js
 */
/* -------------------------------------------------------------------------- */
(function($) {


// speed-up reference to 'window' object
var window = this;

// supplements 'undefined'
window.undefined = window.undefined;

// set default easing method
$.easing.def = 'easeOutCubic';



/* =============== create CWJ JavaScript Library global object and prepartions =============== */
/**
 * CWJ JavaScript Library global object.
 * @name CWJL
 * @namespace CWJL global object
 */
window.CWJL = $.extend(window.CWJL, new (function() {
var d  = document;
var de = d.documentElement;
var di = d.implementation;
var ua = navigator.userAgent;

/**
 * default setting values of CWJL function/classes
 * @name CWJL.settings
 * @namespace default setting values of CWJL function/classes
 * @property {Object}  common                  common settings
 * @property {Boolean} common.useBackCompat    use backword compatible mode
 * @property {Boolean} common.showGeometry     show geometry values in browser's status bar
 */
this.settings        = {};
this.settings.common = { useBackCompat : true, showGeometry : false };

/**
 * identifier urls of frequently used XML-Namespaces.
 * @name CWJL.ns
 * @namespace identifier urls of frequently used XML-Namespaces
 * @property {String} defaultNS    current default namespace of the docuemnt elmeent
 * @property {String} xhtml1       XHTML1 namespace
 * @property {String} xhtml2       XHTML2 namespace
 * @property {String} bajl         BAJL namespace
 */
this.ns              = {};
this.ns.defaultNS    = (!de) ? '' : de.namespaceURI || de.tagUrn || '';
this.ns.xhtml1       = 'http://www.w3.org/1999/xhtml';
this.ns.xhtml2       = 'http://www.w3.org/2002/06/xhtml2';
this.ns.cwjl         = 'http://www.cwj.jp/';

/**
 * browser distinction results.
 * @name CWJL.ua
 * @namespace browser distinction results.
 * @property {String}  versionText     string form of the browser version number (ex: "1.9.0.2", "528.18.1", "7.0")
 * @property {Number}  version         float number of the browser version (ex: 1.9, 528.18, 7.0)
 * @property {Boolean} isGecko         true if the browser is Gecko-based.
 * @property {Boolean} isSafari        true if the browser is AppleWebKit-based
 * @property {Boolean} isOpera         true if the browser is Opera
 * @property {Boolean} isIE            true if the browser is IE-based
 * @property {Boolean} isWin           true if the browser runs in Windows
 * @property {Boolean} isMac           true if the browser runs in MacOS(X)
 * @property {Boolean} isQuirksMode    true if the browser runs under 'quirks mode'
 * @property {Number}  documentMode    number of documentMode; this avaiable in IE, the value is 0 in other browsers.
 * @property {Boolean} isDOMReady      true if the browser had DOM feature.
 */
this.ua              = {};
this.ua.versionText  = $.browser.version;
this.ua.version      = parseFloat($.browser.version);
this.ua.isGecko      = $.browser.mozilla;
this.ua.isSafari     = $.browser.safari;
this.ua.isOpera      = $.browser.opera;
this.ua.isIE         = $.browser.msie;
this.ua.isWin        = /Win/.test(ua);
this.ua.isMac        = /Mac/.test(ua);
this.ua.isQuirksMode = (d.compatMode == 'BackCompat' || this.ua.isIE && this.ua.version < 6);
this.ua.documentMode = (!this.ua.isIE        ) ? 0               :
                       (d.documentMode       ) ? d.documentMode  :
                       (!this.ua.isQuirksMode) ? this.ua.version :
                                                 5               ;
this.ua.isDOMReady   = (di) ? di.hasFeature('HTML', '1.0') : (this.ua.isIE && de)

/**
 * geometry properties object; the property values are updated when {@link CWJL.GetGeometry} is called.
 * @name CWJL.geom
 * @namespace geometry properties object; return value of {@link CWJL.GetGeometry}
 * @property {Number} windowW      width of the window viewport.
 * @property {Number} windowH      height of the window viewport.
 * @property {Number} pageW        width of the document.
 * @property {Number} pageH        height of the document.
 * @property {Number} scrollX      scrollLeft position of the document.
 * @property {Number} scrollY      scrollTop position of the document.
 * @property {Number} windowX      mouse position on X-axis (the origin is top left of the window viewport)
 * @property {Number} windowY      mouse position on Y-axis (the origin is topleft of the window viewport)
 * @property {Number} pageX        mouse position on X-axis (the origin is topleft of the document)
 * @property {Number} pageY        mouse position on Y-axis (the origin is topleft of the document)
 * @property {Number} zoom         window zoom ratio (in WinIE7).
 * @property {Number} scrollBar    browser's scrollbar width.
 */
this.geom            = {};

/**
 * misc environment values.
 * @name CWJL.env
 * @namespace misc environment values.
 * @property {Boolean} isOnline      true if the browser is online.
 * @property {Boolean} isDOMReady    true if the document is ready for DOM manipulation
 */
this.env             = {};
this.env.isOnline    = /^https?\:$/.test(location.protocol);
this.env.isDOMReady  = false;
}));

// 'Node.XXXX_NODE' constants for IE
if (typeof window.Node == 'undefined') {
window.Node = {
  ELEMENT_NODE                : 1
, ATTRIBUTE_NODE              : 2
, TEXT_NODE                   : 3
, CDATA_SECTION_NODE          : 4
, ENTITY_REFERENCE_NODE       : 5
, ENTITY_NODE                 : 6
, PROCESSING_INSTRUCTION_NODE : 7
, COMMENT_NODE                : 8
, DOCUMENT_NODE               : 9
, DOCUMENT_TYPE_NODE          : 10
, DOCUMENT_FRAGMENT_NODE      : 11
, NOTATION_NODE               : 12
};
}

// dummy object of window.console (Firbug etc)
if (typeof window.console != 'object') {
window.console = {
  element           : null
, firebug           : "0"
, userObjects       : {}
, assert            : function(){}
, clear             : function(){}
, count             : function(){}
, debug             : function(){}
, dir               : function(){}
, dirxml            : function(){}
, error             : function(){}
, getFirebugElement : function(){}
, group             : function(){}
, groupCollapsed    : function(){}
, groupEnd          : function(){}
, log               : function(){}
, notifyFirebug     : function(){}
, profile           : function(){}
, profileEnd        : function(){}
, time              : function(){}
, timeEnd           : function(){}
, trace             : function(){}
, warn              : function(){}
};
}

// indicate availability of dom controls
if (CWJL.ua.isDOMReady) {
// prevent background image flicker.
if (CWJL.ua.isIE) try { document.execCommand('BackgroundImageCache', false, true) } catch(err) { }

$(function() {
CWJL.env.isDOMReady = true;
new CWJL.Timeout(function() {
$(document.body).addClass('cwj-enabled');
if (CWJL.settings.common.useBackCompat) $(document.body).addClass('dom-enabled');
if (CWJL.settings.common.showGeometry ) CWJL.GetGeometry.continuously();
}, 1);  // workaround for IE to avoid speed down
});
}






/* =============== custom / shortage methods for built-in objects =============== */

/* ----- Array.indexOf() ----- */

if (!Array.prototype.indexOf) {
/**
 * returns the first index of an element within the array equal to the specified value, or -1 if none is found.
 * (implement emulation of the method defined in JavaScript1.6)
 * @param {Object} aSearchElement    the item to search
 * @param {Number} [aFromIndex]      index number to start searching
 * @return index number
 * @type Number
 */
Array.prototype.indexOf = function(aSearchElement, aFromIndex) {
if (typeof aFromIndex != 'number') {
aFromIndex = 0;
} else if (aFromIndex < 0) {
aFromIndex = this.length + aFromIndex;
}
for (var i = aFromIndex, n = this.length; i < n; i++) {
if (this[i] === aSearchElement) {
return i;
}
}
return -1;
}
}

/* ----- Array.lastIndexOf() ----- */

if (!Array.prototype.lastIndexOf) {
/**
 * returns the last index of an element within the array equal to the specified value, or -1 if none is found.
 * (implement emulation of the method defined in JavaScript1.6)
 * @param {Object} aSearchElement    the item to search
 * @param {Number} [aFromIndex]      index number to start searching
 * @return index number
 * @type Number
 */
Array.prototype.lastIndexOf = function(aSearchElement, aFromIndex) {
if (typeof aFromIndex != 'number') {
aFromIndex = this.length - 1;
} else if (aFromIndex < 0) {
aFromIndex = this.length + aFromIndex;
}
for (var i = aFromIndex; i >= 0; i--) {
if (this[i] === aSearchElement) {
return i;
}
}
return -1;
}
}

/* ----- Array.forEach() ----- */

if (!Array.prototype.forEach) {
/**
 * calls a function for each element in the array.
 * (implement emulation of the method defined in JavaScript1.6)
 * @param {Array.callback.iterate} aCallback        the function to exec for every element
 * @param {Object}                 [aThisObject]    the object that will be a global object ('this') in aCallback func.
 */
Array.prototype.forEach = function(aCallback, aThisObject) {
for (var i = 0, n = this.length; i < n; i++) {
aCallback.call(aThisObject, this[i], i, this);
}
}
}

/* ----- Array.map() ----- */

if (!Array.prototype.map) {
/**
 * creates a new array with the results of calling a provided function on every element in this array.
 * (implement emulation of the method defined in JavaScript1.6)
 * @param {Array.callback.iterate} aCallback        the function to exec for every element
 * @param {Object}                 [aThisObject]    the object that will be a global object ('this') in aCallback func.
 * @return array that consisted of returned values.
 * @type Array
 */
Array.prototype.map = function(aCallback, aThisObject) {
var ret = [];
for (var i = 0, n = this.length; i < n; i++) {
ret.push(aCallback.call(aThisObject, this[i], i, this));
}
return ret;
}
}

/* ----- Array.filter() ----- */

if (!Array.prototype.filter) {
/**
 * creates a new array with all of the elements of this array for which the provided filtering function returns true. 
 * (implement emulation of the method defined in JavaScript1.6)
 * @param {Array.callback.test} aCallback        the function to test all elements
 * @param {Object}              [aThisObject]    the object that will be a global object ('this') in aCallback func.
 * @return array that consisted of only adapted elements.
 * @type Array
 */
Array.prototype.filter = function(aCallback, aThisObject) {
var ret = [];
for (var i = 0, n = this.length; i < n; i++) {
if (aCallback.call(aThisObject, this[i], i, this)) {
ret.push(this[i]);
}
}
return ret;
}
}

/* ----- Array.some() ----- */

if (!Array.prototype.some) {
/**
 * returns true if at least one element in this array satisfies the provided testing function.
 * (implement emulation of the method defined in JavaScript1.6)
 * @param {Array.callback.test} aCallback        the function to test condition of the elements
 * @param {Object}              [aThisObject]    the object that will be a global object ('this') in aCallback func.
 * @return did some elements satisfy the condition?
 * @type Boolean
 */
Array.prototype.some = function(aCallback, aThisObject){
for (var i = 0, n = this.length; i < n; i++) {
if (aCallback.call(aThisObject, this[i], i, this)) return true;
}
return false;
}
}

/* ----- Array.every() ----- */

if (!Array.prototype.every) {
/**
 * returns true if every element in this array satisfies the provided testing function.
 * (implement emulation of the method defined in JavaScript1.6)
 * @param {Array.callback.test} aCallback        the function to test condition of the elements
 * @param {Object}              [aThisObject]    the object that will be a global object ('this') in aCallback func.
 * @return did all elements satisfy the condition?
 * @type Boolean
 */
Array.prototype.every = function(aCallback, aThisObject){
for (var i = 0, n = this.length; i < n; i++) {
if(!aCallback.call(aThisObject, this[i], i, this)) return false;
}
return true;
}
}

/* ----- for JSDoc toolkit output ----- */
/**
 * higher-order functions for member methods of {@link Array}
 * @name Array.callback
 * @namespace higher-order functions for member methods of {@link Array}
 */
/**
 * higher-order function for {@link Array#forEach}, {@link Array#map}
 * @function
 * @name Array.callback.iterate
 * @param {Object} anElement    current processing element of the Array.
 * @param {Number} anIndex      current processing index-num of the Array.
 * @param {Array}  anArray      the Array itself.
 * @returns processing result value (any type) of this function
 * @type Object
 */
/**
 * higher-order function for {@link Array#filter}, {@link Array#some}, {@link Array#every}
 * @function
 * @name Array.callback.test
 * @param {Object} anElement    current processing element of the Array.
 * @param {Number} anIndex      current processing index-num of the Array.
 * @param {Array}  anArray      the Array itself.
 * @returns processing result value (boolean) of this function
 * @type Boolean
 */






/* ============================== CWJ JavaScript Library build-in object wrapper classes ============================== */

/* --------------- Class : CWJL.Number --------------- */
/**
 * get number manipulator {@link CWJL.Number.Wrapper}.
 * @namespace number manipulator
 * @param {Number} [value]    number to manipulate.
 * @return CWJL.Number.Wrapper instance
 * @type CWJL.Number.Wrapper
 */
CWJL.Number = function(value) {
return new CWJL.Number.Wrapper(value);
}

/* ----- Class : CWJL.Number.Wrapper ----- */
/**
 * number manipulator; the instance is available as return value of {@link CWJL.Number}
 * @class number manipulator.
 * @param {Number} [value=0]    number to manipulate.
 * @constructor
 * @see CWJL.Number
 */
CWJL.Number.Wrapper = function(value) {
/** stored number to manipulate
    @type Number
    @private */
this.value = Number(value) || 0;
}

/**
 * get current number as string
 * @returns get current number as string
 * @type String
 */
CWJL.Number.Wrapper.prototype.toString = function() {
return String(this.value);
}

/**
 * get current number
 * @returns get current number
 * @type Number
 */
CWJL.Number.Wrapper.prototype.get = function() {
return this.value;
}

/**
 * simple number formatter.
 * @param {String} format     fomatter string
 * @return string manipulator instance contains result string of this method
 * @type CWJL.String.fn
 * @example
 *  CWJL.Number(     '56'   ).format(      '000'    ).get() ->        '056'
 *  CWJL.Number( '123456'   ).format(      '###'    ).get() ->        '456'
 *  CWJL.Number( '123456.78').format('#,###,###'    ).get() ->    '123,457'
 *  CWJL.Number( '123456.78').format('#,###,###.#'  ).get() ->    '123,456.8'
 *  CWJL.Number('-123456.78').format('0,###,###.000').get() -> '-0,123,456.780'
 */
CWJL.Number.Wrapper.prototype.format = function(format) {
if (!format || typeof format != 'string') {
throw new TypeError('CWJL.Number.Wrapper.format: first argument must be a formatting string.');
} else {
var ret       = [];
var num       = parseFloat(this.value) || 0;
var intFormat = format.split('.')[0].split('');
var decFormat = format.split('.')[1] || '';
var value     = (decFormat) ? Math.abs(num) : Math.round(Math.abs(num));
var sign      = (num < 0) ? '-' : '';
var intValue  = value.toString().split('.')[0].split('');
do {
var _value  = intValue .pop() || '';
var _format = intFormat.pop() || '';
switch (_format) {
case '0' : ret.push(_value  ? _value : '0');                        break;
case '#' : ret.push(_value  ? _value : '' );                        break;
case ''  : /* exit do-while loop */          intValue = [];         break;
default  : ret.push(_format               ); intValue.push(_value); break;
}
} while (intValue.length > 0 || intFormat.length > 0);
ret = ret.reverse().join('').replace(/^\D+/, '');
if (decFormat) {
var scale     = Math.pow(10, decFormat.length);
var rounded   = Math.round(value * scale) / scale;
if (rounded - ret == 1) {
ret++;
}
var decValue  = rounded.toString().split('.')[1] || '0';
    decValue  = decValue .split('').reverse().join('');
    decFormat = decFormat.split('').reverse().join('');
    ret       = ret + '.' + CWJL.Number(decValue).format(decFormat).split('').reverse().join('');
}
if (CWJL.String(decFormat).startsWith('#') && CWJL.String(ret).endsWith('.0')) {
ret = CWJL.String(ret).getBefore('.0');
}
return CWJL.String(sign + ret);
}
}



/* --------------- Class : CWJL.String --------------- */
/**
 * get string manipulator {@link CWJL.String.Wrapper}.
 * @namespace string manipulator
 * @param {String} [value]    string to manipulate.
 * @returns CWJL.String.Wrapper instance
 * @type CWJL.String.Wrapper
 */
CWJL.String = function(value) {
return new CWJL.String.Wrapper(value);
}

/* ----- Class : CWJL.String.Wrapper ----- */
/**
 * creates string manipulator; the instance is available as return value of {@link CWJL.String}
 * @class string manipulator.
 * @param {String} [value='']    string to manipulate.
 * @constructor
 * @see CWJL.String
 */
CWJL.String.Wrapper = function(value) {
/** stored string to manipulate
    @type String
    @private */
this.value  = String(value === undefined ? '' : value);
/** string length
    @type Number */
this.length = this.value.length;
}

/**
 * get current string
 * @returns current string
 * @type String
 */
CWJL.String.Wrapper.prototype.toString = function() {
return this.value;
}

/**
 * get current string
 * @returns current string
 * @type String
 * @function
 */
CWJL.String.Wrapper.prototype.get = CWJL.String.Wrapper.prototype.toString

/**
 * simple text formatter. (super tiny!)
 * @param {String|String[]|Object}  arg1     a string, an array, or an associative array.
 * @param {String}                 [argN]    a string
 * @return this instance itself
 * @type CWJL.String.Wrapper
 * @example
 *  CWJL.String('${0}HOGE${1}FUGA${2}').format(    'xxx', '  yyy',   'zzz'  ).get()
 *  CWJL.String('${0}HOGE${1}FUGA${2}').format([   'xxx',   'yyy',   'zzz' ]).get()
 *  CWJL.String('${A}HOGE${B}FUGA${C}').format({ A:'xxx', B:'yyy', C:'zzz' }).get()
 *      -> 'xxxHOGEyyyFUGAzzz'
 */
CWJL.String.Wrapper.prototype.format = function(arg1, /* arg2, arg3 ..., */ argN) {
var arr;
if (arguments.length == 0) {
return this;
} else if (typeof arg1 == 'object') {
arr = arg1;
} else {
arr = $.makeArray(arguments).map(function(a) { return String(a) });
}

$.each(arr, CWJL.Delegate(function(i, str) {
this.value = this.value.replace(new RegExp('\\$\\{' + i + '\\}', 'g'), str);
}, this));
this.length = this.value.length;

return this;
}

/**
 * get string before specified keyword.
 * @param {String}  str          find keyword
 * @param {Boolean} [include]    if true, 'str' is included in manipulating string
 * @return this instance itself
 * @type CWJL.String.Wrapper
 */
CWJL.String.Wrapper.prototype.getBefore = function(str, include) {
if (typeof str != 'string') {
throw new TypeError('CWJL.String.Wrapper.getBefore: first argument must be a string.');
} else if (str) {
var idx = this.value.indexOf(str);
this.value  = (idx == -1) ? '' : this.value.substring(0, idx) + (include ? str : '');
this.length = this.value.length;
}
return this;
}

/**
 * get string after specified keyword.
 * @param {String}  str          find keyword
 * @param {Boolean} [include]    if true, 'str' is included in manipulating string
 * @return this instance itself
 * @type CWJL.String.Wrapper
 */
CWJL.String.Wrapper.prototype.getAfter = function(str, include) {
if (typeof str != 'string') {
throw new TypeError('String.Wrapper.getAfter: first argument must be a string.');
} else if (str) {
var idx = this.value.indexOf(str);
this.value  =  (idx == -1) ? '' : (include ? str : '') + this.value.substring(idx + str.length, this.value.length);
this.length = this.value.length;
}
return this;
}

/**
 * returns true if the string is started with specified keyword.
 * @param {String}  str    checking keyword
 * @return is the string started with the keyword?
 * @type Boolean
 */
CWJL.String.Wrapper.prototype.startsWith = function(str) {
return (this.value.indexOf(str) == 0);
}

/**
 * returns true if the string is ended with specified keyword.
 * @param {String} str    checking keyword
 * @return is the string ended with the keyword?
 * @type Boolean
 */
CWJL.String.Wrapper.prototype.endsWith = function(str) {
var idx = this.value.lastIndexOf(str);
return (idx > -1 && idx + str.length == this.value.length);
}

/**
 * convert relative path/URL to absolute path/URL.
 * @param {String} base    absolute path/URL as a base.
 * @return this instance itself
 * @type CWJL.String.Wrapper
 * @example
 *  CWJL.String('../target/').rel2abs('/path/to/base/'      ).get() -> '/path/to/target/'
 *  CWJL.String('../target/').rel2abs('http://path/to/base/').get() -> 'http://path/to/target/'
 */
CWJL.String.Wrapper.prototype.rel2abs = function(base) {
var target = this.value;
var b      = base  .split('/');
var t      = target.split('/');
var ptn = /^(\/|\w+:)/;
if (!base.match(ptn)) {
throw new TypeError('CWJL.String.Wrapper.rel2abs: first argument must be an absolute path/URL.');
} else if (target.match(ptn)) {
// do nothing
} else if (target.charAt(0) == '#' || target.charAt(0) == '?') {
this.value = base + target;
} else if (t[0] == '.' || t[0] == '..') {
this.value = CWJL.String(t.slice(1, t.length).join('/')).rel2abs(b.slice(0, b.length - t[0].length).join('/') + '/').get();
} else {
this.value = b.slice(0, b.length - 1).join('/') + '/' + target;
}
this.length = this.value.length;
return this;
}

/**
 * convert absolute path/URL to relative path/URL.
 * @param {String} base    absolute path/URL as a base.
 * @return this instance itself
 * @type CWJL.String.Wrapper
 * @example
 *  CWJL.String(      '/path/to/target/').abs2rel('/path/to/base/'      ).get() -> '../target/'
 *  CWJL.String('http://path/to/target/').abs2rel('http://path/to/base/').get() -> '../target/'
 */
CWJL.String.Wrapper.prototype.abs2rel = function(base) {
var ptn = /^(\/|\w+:)/;
if (!base.match(ptn)) {
throw new TypeError('CWJL.String.Wrapper.abs2rel: first argument must be an absolute path/URL.');
} else if (!this.value.match(ptn)) {
throw new TypeError('CWJL.String.Wrapper.abs2rel: current string is not an absolute path/URL.');
} else {
this.value  =  _compare(base, this.value) || base;
this.length = this.value.length;
}
return this;

function _compare(base, target) {
var b = base  .split('/');
var t = target.split('/');
if (!base) {
return target;
} else if (!target) {
return _goup(base);
} else if (b[0] != t[0]) {
return _goup(base) + target;
} else {
return arguments.callee(b.slice(1, b.length).join('/'), t.slice(1, t.length).join('/'));
}
}

function _goup(path) {
path = path.split('/');
path.shift();
path.forEach(function(elem, idx) { path[idx] = '..' });
return path.join('/') + '/';
}
}

/**
 * convert to sanitized string
 * @return this instance itself
 * @type CWJL.String.Wrapper
 */
CWJL.String.Wrapper.prototype.sanitize = function() {
var pairs = {
  '&'      : '&amp;'
, '<'      : '&lt;'
, '>'      : '&gt;'
, '\u0022' : '&quot;'
, '\u0027' : '&apos;'
};
for (var key in pairs) {
this.value  = this.value.replace(new RegExp(key, 'g'), pairs[key]);
this.length = this.value.length;
}
return this;
}



/* --------------- Class : CWJL.StyleSheets --------------- */
/**
 * get styleSheet wrapper instance.
 * @namespace styleSheet manipulator
 * @param {Number|StyleSheet} [arg]    index number in StyleSheet list / StyleSheet object
 * @return styleSheet wrapper instance
 * @type CWJL.StyleSheets.Wrapper
 * @example
 *  CWJL.StyleSheets().insertRule('body { color: red }');          // set font color to red
 *  CWJL.StyleSheets().each(function() { this.disabled = true });  // diable all style
 */
CWJL.StyleSheets = function(arg) {
var sheets = document.styleSheets;

if (CWJL.ua.isSafari) {
var $cssNode = $('link').filter(function() { return Boolean(this.sheet) });
var dataName = 'CWJL.StyleSheets.Sheet.disabled';

if ($cssNode.length > sheets.length) {
$cssNode.each(function() {
$(this).data(dataName, ($.inArray(this.sheet, sheets) == -1));
this.disabled = true;
this.disabled = false;
});
$.each(sheets, function() {
this.disabled = $(this.ownerNode).data(dataName);
});
}
}

if (typeof arg == 'number') {
sheets = sheets[arg];
} else if (typeof arg == 'object' && arg.type == 'text/css') {
sheets = arg;
}

return new CWJL.StyleSheets.Wrapper(sheets);
}

/* ----- Class : CWJL.StyleSheets.Wrapper ----- */
/**
 * styleSheet wrapper; the instance is available as return value of {@link CWJL.StyleSheets}
 * @class styleSheet wrapper
 * @param {StyleSheetList} [sheets]    styleSheet list
 * @constructor
 */
CWJL.StyleSheets.Wrapper = function(sheets) {
/** number of styleSheets owned by this instance.
    @type Number */
this.length = 0;

$.makeArray(sheets).forEach(function(sheet, i) {
this[i] = sheet;
this.length++;
}, this);
}

/**
 * get new instance that has a styleSheet indicated by index number.
 * @param {Number} [index]    index number to get.
 * @return new instance that has a styleSheet indicated by index number.
 * @type CWJL.StyleSheets.Wrapper
 */
CWJL.StyleSheets.Wrapper.prototype.eq = function(index) {
var sheet = (typeof index == 'number') ? this[index] : null;
return new this.constructor(sheet);
}

/**
 * get DOM StyleSheet object specified by index number, or an array of StyleSheets
 * @param {Number} [index]    index number to get.
 * @return StyleSheet object specified by index number, or an array of StyleSheets
 * @type StyleSheet|Array
 */
CWJL.StyleSheets.Wrapper.prototype.get = function(index) {
return (typeof index == 'number') ? this[index] : Array.prototype.slice.call(this);
}

/**
 * get "ownerNode" of a DOM StyleSheet specified by index number, or an array of ownerNodes of the StyleSheets.
 * @param {Number} [index]    index number to get.
 * @return "ownerNode" of a DOM StyleSheet specified by index number, or an array of ownerNodes of the StyleSheets
 * @type Element|Element[]
 */
CWJL.StyleSheets.Wrapper.prototype.getOwnerNode = function(index) {
if (typeof index == 'number') {
return this[index].ownerNode || this[index].owingElement;
} else {
return Array.prototype.map.call(this, function(e, i) { return this.getOwnerNode(i) }, this);
}
}

/**
 * get number of styleSheets owned by this instance.
 * @return number of styleSheets owned by this instance.
 * @type Number
 */
CWJL.StyleSheets.Wrapper.prototype.size = function() {
return this.length;
}

/**
 * calls a function for each styleSheet in this instance; compatible with jQuery.each().
 * @param {CWJL.StyleSheets.Wrapper.callback.test} aCallback    the function to exec for every styleSheet; if the func returns false, it terminates iteration.
 * @return this instance itself
 * @type CWJL.StyleSheets.Wrapper
 */
CWJL.StyleSheets.Wrapper.prototype.each = function(aCallback) {
$.each(this.get(), aCallback);
return this;
}

/**
 * filter styleSheets in this instance; compatible with jQuery.filter().
 * @param {CWJL.StyleSheets.Wrapper.callback.test} aCallback    the function to exec for every styleSheet; if the func returns false, the styleSheet is filtered.
 * @return new instance that has filtered styleSheets.
 * @type CWJL.StyleSheets.Wrapper
 */
CWJL.StyleSheets.Wrapper.prototype.filter = function(aCallback) {
return new this.constructor(this.get().filter(function(s, i) { return aCallback.call(s, i, s) }));
}

/**
 * insert css rule; the rule insert to the first styleSheet in this instance.
 * @param {String} cssText    css rule text to add
 * @return this instance itself
 * @type CWJL.StyleSheets.Wrapper
 */
CWJL.StyleSheets.Wrapper.prototype.insertRule = function(cssText) {
var expr  = /([^\{]+)(\{.+\})/;
var sheet = this.get(0);
if (!sheet) {
return;
} else if (!expr.test(cssText)) {
throw new TypeError('CWJL.StyleSheets.Wrapper.insertRule : first argument must be a style rule text.');
} else {
var isOldSafari = (CWJL.ua.isSafari && CWJL.ua.version < 522); /* Safari 2.0.x or ealier */
if (isOldSafari || (!sheet.insertRule && !sheet.addRule)) {
var style = document.createElement('style');
style.type= 'text/css';
style.appendChild(document.createTextNode(cssText));
document.getElementsByTagName('head')[0].appendChild(style);
} else if (sheet.insertRule) {  // Std DOM.
sheet.insertRule(cssText, sheet.cssRules.length);
} else if (sheet.addRule) {     // IE
var regex     = expr.exec(cssText);
var selector  = $.trim(regex[1]);
var predicate = $.trim(regex[2]);
sheet.addRule(selector, predicate);
}
return this;
}
}

/* ----- for JSDoc toolkit output ----- */
/**
 * higher-order functions for member methods of {@link CWJL.StyleSheets.Wrapper}
 * @name CWJL.StyleSheets.Wrapper.callback
 * @namespace higher-order functions for member methods of  {@link CWJL.StyleSheets.Wrapper}
 */
/**
 * higher-order function for {@link CWJL.StyleSheets.Wrapper#each}, {@link CWJL.StyleSheets.Wrapper#filter}
 * @function
 * @name CWJL.StyleSheets.Wrapper.callback.test
 * @param {Number}      anIndex      current processing index-num of the styleSheet.
 * @param {StyleSheet}  anSheet      current styleSheet.
 * @returns processing result value (boolean) of this function
 * @type Boolean
 */



/* --------------- Class : CWJL.OpenWindow --------------- */
/**
 * open new window.
 * @namespace window opener
 * @param {String}  url               url to open in new window
 * @param {String}  [target="_blank"] window target name
 * @param {Number}  [width]           width of new window
 * @param {Number}  [height]          height of new window
 * @param {String}  [options]         window options wo/width,height
 * @param {Boolean} [moveFlag]        if true, the new window moves to left top of the screen
 * @return new window object
 * @type Window
 */
CWJL.OpenWindow = function(url, target, width, height, options, moveFlag) {
if (typeof url != 'string' || !url) {
throw new URIError('CWJL.OpenWindow: first argument must be a string (url).');
} else {
var _this    = arguments.callee;
var target   = (typeof target == 'string' && target) ? target : '_blank';
var preset   = _this.getPreset(target);
var options  = preset.options  || _this.parseOptions(options);
var moveFlag = preset.moveFlag || Boolean(moveFlag);

options.width  = preset.width  || width  || options.width  || undefined;
options.height = preset.height || height || options.height || undefined;
if (!(options.width  > 0)) delete options.width;
if (!(options.height > 0)) delete options.height;

var optArr = [];
for (var key in options) optArr.push(key + '=' + options[key]);
var newWin = window.open(url, target, optArr.join(','));
newWin.focus();

if (moveFlag    ) newWin.moveTo(0, 0);
if (window.event) window.event.returnValue = false;
return newWin;
}
}

/**
 * preset data storage.
 * @type Object
 * @private
 */
CWJL.OpenWindow.presets = {};

/**
 * get preset data in associative array format
 * @return preset data (associative array)
 * @type Object
 */
CWJL.OpenWindow.getPreset = function(target) {
if (typeof target != 'string' || !target) {
return {};
} else {
return this.presets[target] || {};
}
}

/**
 * add setting data that related to link target name 
 * @param {String}  target         window target name
 * @param {Number}  [width]        width of new window
 * @param {Number}  [height]       height of new window
 * @param {String}  [options]      window options wo/width,height
 * @param {Boolean} [moveFlag]     if true, the new window moves to left top of the screen
 * @return preset data (associative array)
 * @type Object
 */
CWJL.OpenWindow.addPreset = function(target, width, height, options, moveFlag) {
if (typeof target != 'string' || !target) {
throw new TypeError('CWJL.OpenWindow,addPreset: first argument (target) must be a string.');
} else {
var preset      = this.presets[target] = {};
var options     = this.parseOptions(options);
preset.width    = (width  > 0) ? width  : (options.width  > 0) ? options.width  : undefined;
preset.height   = (height > 0) ? height : (options.height > 0) ? options.height : undefined;
preset.options  = options;
preset.moveFlag = Boolean(moveFlag);

delete preset.options.width;
delete preset.options.height;

return preset;
}
}

/**
 * parse options text, and change into an associative array.
 * @return associative array
 * @type Object
 * @private
 */
CWJL.OpenWindow.parseOptions = function(optionsStr) {
if (typeof optionsStr != 'string' || !optionsStr) {
return {};
} else {
var options = {};
optionsStr.replace(/\s/g, '').split(',').forEach(function(pair) {
var arr = pair.split('=');
if (arr[0]) options[arr[0]] = arr[1] || '';
});
return options;
}
}

/**
 * open new fullscreen window.
 * @param {String} url                   url to open in new window
 * @param {String} [target="_blank"]     window target name
 * @param {String} [options]             window options wo/width,height
 * @return new window object
 * @type Window
 * @see CWJL.OpenWindow
 */
CWJL.OpenWindow.full = function(url, target, options) {
options = options || 'toolbar=no,location=no,directories=no,status=no,menubar=no,scrollbars=no,resizable=no';
return CWJL.OpenWindow(url, target, screen.availWidth, screen.availHeight, options, true);
}






/* ============================== CWJL classes ============================== */

/* -------------------- Class : CWJL.Observable -------------------- */
/**
 * create observable object
 * @class observable
 * @constructor
 */
CWJL.Observable = function() {
/** callback function chain, pair of key name and function.
    @type Object
    @private */
this.callbackChains      = null;
/** callback ignoring level - pairs of callback name and level string; 'all', 'preserved', 'disposable', 'none'.
    @type Object
    @private */
this.callbackIgnoreLevel = {};
}

/**
 * process callback functions.
 * @param {String} name            callback name (preferred to start with 'on')
 * @param {Object} [_arguments]    arguments for callback function
 * @return result value of last one of the callback functions chain.
 * @type Object
 */
CWJL.Observable.prototype.doCallback = function(name, /* arg1, arg2, ... */ _arguments) {
var chain = this.callbackChains;
if (!chain || !chain[name]) {
return undefined;
} else {
var ret;
var args = Array.prototype.slice.call(arguments, 1);

chain[name]
.filter(function(delegate) {
var level = this.callbackIgnoreLevel[name] || 'none';
switch (level) {
case 'preserved'  : return  delegate.isDisposable;
case 'disposable' : return !delegate.isDisposable;
case 'all'        : return  false;
case 'none'       : return  true;
default           : return  true;
}
}, this)
.forEach(function(delegate) {
ret = delegate.apply(null, args);
});

this.removeDisposableCallback(name);
return ret;
}
}

/**
 * add callback function.
 * @param {String}   name             callback name (preferred to start with 'on')
 * @param {Function} func             callback function/method
 * @param {Object}   [aThisObject]    object that will be a global object ('this') in func
 * @param {String}   [disposable]     if 'disposable' specified, the callback function is treated as 'disposable'.
 * @return this instance
 * @type CWJL.Observable
 */
CWJL.Observable.prototype.addCallback = function(name, func, aThisObject, disposable) {
if (typeof name != 'string' || name == '') {
throw new TypeError('CWJL.Observable.addCallback: argument \'name\' must be a string as callback name.');
} else if (typeof func != 'function') {
throw new TypeError('CWJL.Observable.addCallback: argument \'func\' must be a Function object.');
} else {
if (!this.callbackChains      ) this.callbackChains       = {};
if (!this.callbackChains[name]) this.callbackChains[name] = [];

var delegate = CWJL.Delegate(func, aThisObject);
delegate.isDisposable = (disposable == 'disposable');
this.callbackChains[name].push(delegate);
}
return this;
}

/**
 * remove callback function.
 * @param {String}   name             callback name (preferred to start with 'on')
 * @param {Function} [func]           callback function/method to remove, if no funcs given, all callback funcs will be removed.
 * @param {Object}   [aThisObject]    object that will be a global object ('this') in func
 * @return this instance
 * @type CWJL.Observable
 */
CWJL.Observable.prototype.removeCallback = function(name, func, aThisObject) {
var chain = this.callbackChains;
if (typeof name != 'string' || name == '') {
throw new TypeError('CWJL.Observable.removeCallback: argument \'name\' must be a string as callback name.');
} else if (chain && chain[name]) {
if (typeof func != 'function') {
delete chain[name];
} else {
var obj = (typeof aThisObject == 'object' && aThisObject) ? aThisObject : window;
chain[name] = chain[name].filter(function(delegate) {
return (delegate.func !== func || delegate.aThisObject !== obj);
});
}
}
return this;
}

/**
 * remove 'disposable' callback function.
 * @param {String} name    callback name (preferred to start with 'on')
 * @private
 */
CWJL.Observable.prototype.removeDisposableCallback = function(name) {
var chain = this.callbackChains;
if (chain && chain[name]) {
chain[name] = chain[name].filter(function(delegate) { return !delegate.isDisposable });
}
}

/**
 * set callback ignoring level.
 * @param {String} name             callback name (preferred to start with 'on')
 * @param {String} [level="all"]    ignoring level - 'all', 'preserved', 'disposable', 'none'
 * @return this instance
 * @type CWJL.Observable
 */
CWJL.Observable.prototype.ignoreCallback = function(name, level) {
var chain = this.callbackChains;
if (typeof name != 'string' || name == '') {
throw new TypeError('CWJL.Observable.ignoreCallback: argument \'name\' must be a string as callback name.');
} else if (chain && chain[name]) {
var levels = ['all', 'preserved', 'disposable', 'none'];
this.callbackIgnoreLevel[name] = (levels.indexOf(level) != -1) ? level : 'all';
}
return this;
}

if (CWJL.settings.common.useBackCompat) {
/** @deprecated use {@link #doCallback} method instead of this method. */
CWJL.Observable.prototype.doCallBack = function() { return this.doCallback.apply(this, arguments) }
/** @deprecated use {@link #addCakkback} method instead of this method. */
CWJL.Observable.prototype.addCallBack = function() { return this.addCallback.apply(this, arguments) }
/** @deprecated use {@link #removeCallback} method instead of this method. */
CWJL.Observable.prototype.removeCallBack = function() { return this.removeCallback.apply(this, arguments) }
/** @deprecated use {@link #removeDisposableCallback} method instead of this method. */
CWJL.Observable.prototype.removeDisposableCallBacks = function() { return this.removeDisposableCallback.apply(this, arguments) }
}



/* --------------- Class : CWJL.Iterator --------------- */
/**
 * create iterator object
 * @class iterator
 * @param {Object} obj               iterated elements; an associative array, an array, or an object like those.
 * @param {Object} [mode="value"]    element getting mode; 'key', 'value', or 'both'
 */
CWJL.Iterator = function(obj, mode) {
/** iterated elements; an associative array, an array, or an object like those.
    @type Object
    @private
    @constant */
this.targets = obj;
/** an array of keys in iterated elements.
    @type String[]
    @private
    @constant */
this.keys    = [];
/** a number of current position of the iterator.
    @type Number
    @private */
this.counter = 0;
/** element getting mode; "key", "value", or "both".
    @type String
    @constant
    @private */
this.mode    = mode || 'value';

if (arguments.length > 0) {
this.init();
}
}

/**
 * initialize
 * @private
 */
CWJL.Iterator.prototype.init = function() {
if (!this.targets || typeof this.targets != 'object') {
throw new TypeError('CWJL.Iterator#init: invalid object type.');
} else {
$.each(this.targets, CWJL.Delegate(function(key) { this.keys.push(key) }, this));
}
}

/**
 * does the iterator has next element?
 * @return true if the iterator has next element.
 * @type Boolean
 */
CWJL.Iterator.prototype.hasNext = function() {
return (this.counter < this.keys.length);
}

/**
 * get next element in the iteration; the form of an acquired element depends on "element getting mode"
 * @return next element in the iteration
 * @type Object
 */
CWJL.Iterator.prototype.next = function() {
if (!this.hasNext()) {
throw new ReferenceError('BAJL.Iterator#next: StopIteration');
} else {
var key   = this.keys[this.counter++];
var value = this.targets[key];
switch(this.mode) {
case 'key'   : return key;
case 'value' : return value;
case 'both'  : return [key, value];
default      : return undefined;
}
}
}

/*
 * @param {Function} func             callback function
 * @param {Number}   [ms=0]           milliseconds to interval
 * @param {Object}   [aThisObject]    the object that will be a global object ('this') in the func
 */
CWJL.Iterator.prototype.iterate = function(func, ms, aThisObject) {
if (typeof func != 'function') {
throw new TypeError('CWJL.Iterator#iterate: first argument must be a function object.');
} else {
var flag = (this.hasNext()) ?
           func.apply(aThisObject, $.makeArray(this.next())) :
           false;
if (flag !== false) {
if (ms > 0) {
new CWJL.Timeout(function() {
this.iterate(func, ms, aThisObject);
}, ms, this);
} else {
this.iterate(func, ms, aThisObject);
}
}
}
}



/* --------------- Class : CWJL.Timeout --------------- */
/**
 * a wrapper of 'setTimeout()'.
 * @class timeout timer
 * @param {Function} func             callback function
 * @param {Number}   [ms=0]           milliseconds to timeout
 * @param {Object}   [aThisObject]    the object that will be a global object ('this') in the func
 * @constructor
 */
CWJL.Timeout = function(func, ms, aThisObject) {
/** timer ID.
    @type Number
    @private */
this.timer       = 0;
/** callback function
    @type Function
    @private
    @constant */
this.func        = func;
/** milliseconds to timeout
    @type Function
    @private
    @constant */
this.ms          = Math.max(0, ms) || 0
/** the object that will be a global object ('this') in the func
    @type Object
    @private
    @constant */
this.aThisObject = aThisObject || window;
/** native timer function name; 'setTimeout' or 'setInterval'
    @type String
    @private
    @constant */
this.timerFunc   = 'setTimeout';

if (arguments.length) {
this.init();
}
}

/**
 * initialize.
 * @private
 */
CWJL.Timeout.prototype.init = function() {
if (typeof this.func != 'function') {
throw new ReferenceError('CWJL.Timeout.init: callback function is not given.');
} else {
var delegate  = CWJL.Delegate(this.func, this.aThisObject);
var timerFunc = window[this.timerFunc];
this.timer    = (CWJL.ua.isIE) ?
              timerFunc(delegate, this.ms, 'JScript') : // workaround to the page weaved with vbscript.
              timerFunc(delegate, this.ms           ) ;
}
}

/**
 * clear timer.
 */
CWJL.Timeout.prototype.clear = function() {
clearTimeout (this.timer);
clearInterval(this.timer);
}

/** @deprecated use {@link #clear} method instead of this method. */
CWJL.Timeout.prototype.clearTimer = function() { return this.clear.apply(this, arguments) }



/* --------------- Class : CWJL.Interval --------------- */
/**
 * a wrapper of 'setInterval()'.
 * @class interval timer
 * @extends CWJL.Timeout
 * @param {Function} func             callback function
 * @param {Number}   [ms=0]           milliseconds to interval
 * @param {Object}   [aThisObject]    the object that will be a global object ('this') in the func
 * @constructor
 */
CWJL.Interval = function(func, ms, aThisObject) {
/** timer ID.
    @type Number
    @private */
this.timer       = 0;
/** callback function
    @type Function
    @private
    @constant */
this.func        = func;
/** milliseconds to timeout
    @type Function
    @private
    @constant */
this.ms          = Math.max(0, ms) || 0
/** the object that will be a global object ('this') in the func
    @type Object
    @private
    @constant */
this.aThisObject = aThisObject || window;
/** native timer function name; 'setTimeout' or 'setInterval'
    @type String
    @private
    @constant */
this.timerFunc   = 'setInterval';

if (arguments.length) {
this.init();
}
}
CWJL.Interval.prototype = new CWJL.Timeout;



/* --------------- Class : CWJL.Timer --------------- */
/**
 * construct simple elapsed timer object.
 * @class simple elapsed timer.
 * @constructor
 */
CWJL.Timer = function() {
/** started time of the timer in 'epoch time'.
    @type Number
    @private */
this.startTime = null;

this.reset();
}

/**
 * reset timer.
 */
CWJL.Timer.prototype.reset = function() {
this.startTime = (new Date()).getTime();
}

/**
 * get acquire time progress in milisecond.
 * @return acquire time progress in milisecond.
 * @type Number
 */
CWJL.Timer.prototype.getTime = function() {
return (new Date()).getTime() - this.startTime;
}

/**
 * get acquire time progress in second.
 * @return acquire time progress in second.
 * @type Number
 */
CWJL.Timer.prototype.getSeconds = function() {
return this.getTime() / 1000;
}



/* --------------- Class : CWJL.Tag --------------- */
/**
 * create tag string object for document.write().
 * @class tagstring as element object.
 * @constructor
 * @param {String} tagName    element name to create
 * @param {Object} [attrs]    associative array of attributes { name1 : value1, name2 : value2 ... }
 */
CWJL.Tag = function(tagName, attrs) {
/** tag name (element name) to create
    @type String
    @constant */
this.tagName    = tagName;
/** associative array of attributes { name1 : value1, name2 : value2 ... }
    @type Object */
this.attributes = attrs || {};
/** array of {@link BAJL.Tag} instances
    @type BAJL.Tag[] */
this.childNodes = [];
}

/**
 * get/set attribute value.
 * @param {String} name    attribute name
 * @param {String} [value]    value to set
 */
CWJL.Tag.prototype.attr = function(name, value) {
if (typeof name != 'string') {
throw new TypeError('CWJL.Tag.attr: first argument must be a string (name).');
} else if (value == undefined) {
return this.attributes[name] || '';
} else {
return (this.attributes[name] = String(value));
}
}

/**
 * append child instance.
 * @param {CWJL.Tag|String} arg     instance to append
 */
CWJL.Tag.prototype.append = function(arg) {
if (arg == undefined || arg == null) {
throw new TypeError('CWJL.Tag.append: first argument must be a string or a CWJL.Tag instance.');
} else {
if (arg.constructor != this.constructor) {
arg = String(arg);
}
this.childNodes.push(arg);
}
}

/**
 * output instance data as tag string. typically to use document.write().
 * @param {Boolean} [debug=false]    debug mode - escaped output
 * @return HTML tag string
 * @type String
 */
CWJL.Tag.prototype.toString = function(debug) {
var tagOpen    = (debug) ? '&lt;' : '<';
var tagClose   = (debug) ? '&gt;' : '>';
var tag        = tagOpen + this.tagName;
var content    = (this.childNodes.length) ? '' : null;
for (var i = 0, n = this.childNodes.length; i < n; i++) {
content += this.childNodes[i].toString(debug);
}
for (var attr in this.attributes) {
tag += ' ' + attr + '="' + this.attributes[attr] + '"';
}
tag += (content != null) ?
tagClose + content + tagOpen + '/' + this.tagName + tagClose :
' /' + tagClose;
return tag;
}

if (CWJL.settings.common.useBackCompat) {
/** @deprecated use {@link #append} method instead of this method. */
CWJL.Tag.prototype.setAttributeCWJ = function() { return this.attr.apply(this, arguments) }
/** @deprecated use {@link #append} method instead of this method. */
CWJL.Tag.prototype.appendChildCWJ = function() { return this.append.apply(this, arguments) }
}



/* ============================== CWJ JavaScript Library static functions ============================== */

/* --------------- Function : CWJ JavaScript Library. CreateBackCompat --------------- */
/**
 * create references from old name to new name; for backword compatibility.
 * @param {Object} pairs    associative array of pairs of deprecated old name and it's new object
 * @deprecated this func is available for a limited time.
 */
CWJL.CreateBackCompat = function(pairs) {
$.each(pairs, function(key, value) { CWJL.SetValue(key, value) });
}



/* --------------- Function : CWJL.SetValue --------------- */
/**
 * set value to deep object directly.
 * @param {String} expr            object expression string to set value
 * @param {Object} [value]         value to set
 * @param {Object} [obj=window]    base object of expr
 * @returns value that set
 * @type Object
 */
CWJL.SetValue = function(expr, value, obj) {
if (typeof expr != 'string' || !expr) {
throw new TypeError('CWJL.SetValue: first argument type must be string (expr).');
} else if (obj !== null) {
var props = expr.split('.');
var obj   = (obj === undefined) ? window : obj
if (props.length == 1) {
return (obj[expr] = value);
} else {
var prop = props.shift();
if (obj[prop] == undefined) {
obj[prop] = {};
}
return arguments.callee(props.join('.'), value, obj[prop]);
}
}
}



/* --------------- Function : CWJL.GetValue --------------- */
/**
 * get value from deep object directly.
 * @param {String} expr            object expression string to get value
 * @param {Object} [obj=window]    base object of expr
 * @returns any type of value
 * @type Object
 */
CWJL.GetValue = function(expr, obj) {
if (typeof expr != 'string' || !expr) {
throw new TypeError('CWJL.GetValue: first argument type must be string (expr).');
} else {
var props = expr.split('.');
var obj   = (typeof obj != 'object' || !obj) ? window : obj;
if (props.length == 1) {
return obj[expr];
} else {
var prop = props.shift();
return (obj[prop] == undefined) ? undefined : arguments.callee(props.join('.'), obj[prop]);
}
}
}



/* --------------- Function : CWJL.Singleton --------------- */
/**
 * create object as single instance. or put existing instance.
 * @param {Function} _constructor    constructor
 * @param {Object}   [_arguments]    arguments for constructor
 * @return single instance.
 * @type Object
 */
CWJL.Singleton = function(_constructor, /* arg1, arg2, ... */ _arguments) {
if (typeof _constructor != 'function') {
throw new TypeError('CWJL.Singleton: first argument must be a constructor function.');
} else {
//var args = Array.prototype.slice.call(arguments, 1);
return _constructor.__CWJL_SingleInstance__ || (_constructor.__CWJL_SingleInstance__ = new _constructor());
}
}



/* --------------- Function : CWJL.Delegate --------------- */
/**
 * create delegate function to change 'this' scope.
 * @param {Function} func             function for delegate
 * @param {Object}   [aThisObject]    the object that will be a global object ('this') in the function
 * @return delegated function.
 * @type Function
 */
CWJL.Delegate = function(func, aThisObject) {
if (typeof func != 'function') {
throw new TypeError('CWJL.Delegate: first argument must be a function object.');
} else {
var aThisObj = aThisObject || window;
var delegate = function(){
return func.apply(aThisObj, arguments);
};
delegate.func        = func;
delegate.aThisObject = aThisObj;
return delegate; 
}
}



/* --------------- Function : CWJL.AlreadyApplied --------------- */
/**
 * return 'true' if the function is already applied.
 * @param {Function} func    typically 'arguments.callee'
 * @return boolean
 * @type Boolean
 */
CWJL.AlreadyApplied = function(func) {
if (typeof func != 'function') {
throw new TypeError('CWJL.AlreadyApplied: first argument must be a function object.');
} else {
return func.__CWJL_AlreadyApplied__ || !(func.__CWJL_AlreadyApplied__ = true);
}
}



/* --------------- Function : CWJL.PreloadImage --------------- */
/**
 * create Image object (load image).
 * @param {String} src     image url to load
 * @return image object
 * @type Image
 */
CWJL.PreloadImage = function(src) {
if (typeof src != 'string' || !src) {
throw new TypeError('CWJL.PreloadImage: first argument must be a string (img src).');
} else {
var img = new Image;
img.src = src;
return img;
}
}



/* --------------- Function : CWJL.GetGeometry --------------- */
/**
 * get window geometry and mouse position.
 * @param {Event} e    event object - this param exists when this function is called as an event handler.
 * @returns an associative array of geometry properties
 * @type CWJL.geom
 */
CWJL.GetGeometry = function(e) {
var w = window;
var d = document.documentElement;
var b = document.body;
var g = CWJL.geom;
var _ = arguments.callee;

var isWinIEqm = (CWJL.ua.isIE && CWJL.ua.isWin && CWJL.ua.isQuirksMode);
var isMacIE   = (CWJL.ua.isIE && CWJL.ua.isMac);
var isSafari2 = (CWJL.ua.isSafari && CWJL.ua.version < 522); /* Safari 2.0.x or ealier */

g.windowW   = w.innerWidth  || (isMacIE ? b.scrollWidth  : d.offsetWidth );
g.windowH   = w.innerHeight || (isMacIE ? b.scrollHeight : d.offsetHeight);
g.pageW     = (isMacIE) ? d.offsetWidth  : (isWinIEqm) ? b.scrollWidth  : d.scrollWidth ;
g.pageH     = (isMacIE) ? d.offsetHeight : (isWinIEqm) ? b.scrollHeight : d.scrollHeight;
g.scrollX   = w.scrollX || d.scrollLeft || b.scrollLeft || 0;
g.scrollY   = w.scrollY || d.scrollTop  || b.scrollTop  || 0;
g.windowX   = (!e) ? (g.windowX || 0) : e.clientX - (( isSafari2) ? g.scrollX : 0);
g.windowY   = (!e) ? (g.windowY || 0) : e.clientY - (( isSafari2) ? g.scrollY : 0);
g.pageX     = (!e) ? (g.pageX   || 0) : e.clientX + ((!isSafari2) ? g.scrollX : 0);
g.pageY     = (!e) ? (g.pageX   || 0) : e.clientY + ((!isSafari2) ? g.scrollY : 0);
g.zoom      = _.getZoomRatio();
g.scrollBar = _.getScrollBarWidth();

if (CWJL.settings.common.showGeometry) {
var msg = [
  ['window'   , '${windowW}x${windowH}']
, ['page'     , '${pageW}x${pageH}'    ]
, ['scroll'   , '${scrollX},${scrollY}']
, ['pos(view)', '${windowX},${windowY}']
, ['pos(abs)' , '${pageX},${pageY}'    ]
, ['zoom'     , '${zoom}'              ]
, ['sbar'     , '${scrollBar}'         ]
].map(function(a) { return a.join(': ') }).join(' | ');
window.status = CWJL.String(msg).format(g).get();
}
if (CWJL.settings.common.useBackCompat) {
g.mouseX = g.pageX;
g.mouseY = g.pageY;
}

return g;
}

/**
 * get window zoom ratio for IE7+
 * @returns window zoom ratio on IE7+ / always 1 on other browsers.
 * @type Number
 * @private
 */
CWJL.GetGeometry.getZoomRatio = function() {
var d = document.documentElement;
var b = document.body;
var _ = arguments.callee;
_._cache_ = _._cache_ || 1;
if (CWJL.ua.isIE && CWJL.ua.version >= 7.0) {
if (_._lock_) {
_._lock_.clear();
_._lock_ = new CWJL.Timeout(function() { _._lock_ = null }, 500);
} else {
_._lock_ = new CWJL.Timeout(function() { _._lock_ = null }, 500);
if (CWJL.ua.isQuirksMode) {
_._cache_ = d.offsetWidth / b.offsetWidth;
} else {
var id  = 'CWJL_GetGeometry_getZoomRatio_TestNode';
var css = {
  'position'   : 'absolute'
, 'left'       : '0px'
, 'top'        : '0px'
, 'width'      : (d.scrollWidth * 10) + 'px'
, 'height'     : '10px'
, 'background' : 'red'
};
var node = $('#' + id).get(0) || $(document.createElement('ins')).attr('id', id).css(css).appendTo(document.body).get(0);
$(node).show();
_._cache_ = d.scrollWidth / node.offsetWidth;
$(node).hide();
}
}
}
return _._cache_;
}

/**
 * get scrollbar width
 * @return scrollbar width in px unit
 * @type Number
 * @private
 */
CWJL.GetGeometry.getScrollBarWidth = function() {
var _ = arguments.callee;
if (typeof _._cache_ != 'number') {
var id  = 'CWJJL_GetGeometry_getScrollBarWidth_TestNode';
var css = {
  'position'   : 'absolute'
, 'left'       : '-10000px'
, 'top'        : '-10000px'
, 'display'    : 'block'
, 'visibility' : 'hidden'
, 'overflow'   : 'scroll'
, 'width'      : '100px'
, 'height'     : '100px'
, 'border'     : 'none'
, 'margin'     : '0'
, 'padding'    : '0'
};
var node = $('#' + id).get(0) || $(document.createElement('ins')).attr('id', id).css(css).appendTo(document.body).get(0);
_._cache_ = node.offsetWidth - node.clientWidth;
$(node).remove();
}
return _._cache_;
}

/**
 * measuring the geometry continuously
 */
CWJL.GetGeometry.continuously = function() {
if (!CWJL.AlreadyApplied(arguments.callee)) {
CWJL.GetGeometry();
$(document).mousemove(CWJL.GetGeometry);
}
}



/* --------------- Function : CWJL.GetCommonDir --------------- */
/**
 * get URL of common directory.
 * @param {String} dirName     name of common directory
 * @return absolute URL of common directory.
 * @type String
 */
CWJL.GetCommonDir = function(dirName) {
if (typeof dirName != 'string' || !dirName) {
throw new TypeError('CWJL.GetCommonDir: first argument must be a string (directory name to find).');
} else {
var path  = './';
var nodes = document.getElementsByTagName('head')[0].getElementsByTagName('script');
for (var i = 0, n = nodes.length; i < n; i++) {
var arr = nodes[i].src.split('/');
var idx = arr.indexOf(dirName);
if (idx != -1) {
path = arr.slice(0, idx + 1).join('/') + '/';
break;
}
}
if (!path.match(':')) {   // if path is not absolute url (workaround for IE)
var base = document.getElementsByTagName('base')[0];
var burl = (base) ? base.href : location.href;
if (CWJL.String(path).startsWith('/')) {
path = burl.match(/^\w+:\/*[^\/]+/)[0] + path;
} else {
path = CWJL.String(path).rel2abs(burl).get();
}
}
return path;
}
}






/* =============== additional jQuery plugin methods =============== */

/* -------------------- jQuery.fn : BAJL_NormalizeTextNode -------------------- */
/**
 * remove text nodes that have only white spaces, and normalize space of each text nodes.
 * @param {Boolean} [deep]    process recursive node tree?
 * @return jQuery current context object
 * @type jQuery
 */
jQuery.fn.CWJL_NormalizeTextNode = function(deep) {
this.contents().each(function() {
if (this.nodeType == Node.TEXT_NODE) {
if ((this.nodeValue = jQuery.trim(this.nodeValue)) == '') {
this.parentNode.removeChild(this);
}
} else if (deep && this.nodeType == Node.ELEMENT_NODE) {
jQuery(this).CWJL_NormalizeTextNode(deep);
}
});
return this;
}



/* -------------------- jQuery.fn : CWJL_HasElement -------------------- */
/**
 * check if the current context of jQuery objecdt has Element node(s)?
 * @return if the jQuery object has Element node(s).
 * @type Boolean
 */
jQuery.fn.CWJL_HasElement = function() {
return (this.size() > 0 && this.get(0) && this.get(0).nodeType == Node.ELEMENT_NODE);
}



/* -------------------- jQuery.fn : CWJL_AddBeforeUnload -------------------- */
/**
 * temporary wrapper for adding event listener of "window.onBeforeUnload".
 * @param {Function} listener         an event listener function
 * @param {Object}   [aThisObject]    the object that will be a global object ('this') in the listener func.
 */
jQuery.fn.CWJL_AddBeforeUnload = function(listener, aThisObject) {
if (this.get(0) === window) {
if (window.addEventListener) {    // except IE8 and above
window.addEventListener('beforeunload', CWJL.Delegate(listener, aThisObject), false);
} else {
this.bind('beforeunload', function(e) {
if (e && e.originalEvent) {    // prevent error on IE (since jQuery1.4)
listener.call(aThisObject, e.originalEvent);
}
});
}
}
}






/* =============== for backward compatibilities =============== */

if (CWJL.settings.common.useBackCompat) {
// define old CWJDOM methods
var _CWJDOM = {
instanceOf : 'CWJElement',
addEventListenerCWJ : function(type, func, aThis) {
$(this).bind(type, function(e) {
$(e.target       )._setCWJDOM();
$(e.currentTarget)._setCWJDOM();
$(e.relatedTarget)._setCWJDOM();
func.call(aThis || this, e);
});
},
removeEventListenerCWJ : function(type, func) {
$(this).unbind(type, func);
},
dispatchEventCWJ : function(e) {
$(this).trigger(e.type);
}
};
var _CWJWindow   = $.extend(null, _CWJDOM, {});
var _CWJDocument = $.extend(null, _CWJDOM, {
getElementsByTagNameCWJ : function(tagName) {
return $(tagName, this)._setCWJDOM().get();
},
getElementsByClassNameCWJ : function(className, tagName) {
return $([ tagName, className ].join('.'), this)._setCWJDOM().get();
},
getElementsByNameCWJ : function(n) {
return $('[name=' + n + ']', this)._setCWJDOM().get();
},
getElementByIdCWJ : function(id) {
return $('#' + id)._setCWJDOM().get(0);
},
createElementCWJ : function(tagName) {
return $(document.createElement(tagName))._setCWJDOM().get(0);
},
createDocumentFragmentCWJ : function() {
var node = $(document.createElement('ins'))._setCWJDOM().get(0);
$.data(node, 'CWJDocument.PseudoDocumentFragmentNode', true);
return node;
},
isAncestorOfCWJ : function(node) {
var _this = this;
return $(node).parents().get().some(function(_node) { return (_node == _this) });
},
getStyleSheetsCWJ : function() {
return CWJL.StyleSheets().get().map(function(sheet) {
/** @ignore */
sheet.addRuleCWJ = function(cssText) { CWJL.StyleSheets(this).insertRule(cssText) };
return sheet;
});
}
});
var _CWJElement = $.extend(null, _CWJDocument, {
getAncestorsByTagNameCWJ : function(tagName) {
return $(this).parents(tagName)._setCWJDOM().get();
},
getAncestorsByClassNameCWJ : function(className, tagName) {
return $(this).parents([ tagName, className ].join('.'))._setCWJDOM().get();
},
getParentNodeCWJ : function() {
return $(this).parent()._setCWJDOM().get(0);
},
getChildNodesCWJ : function() {
return $(this).contents()._setCWJDOM().get();
},
appendChildCWJ : function(content, forceAsHTML) {
if (typeof content == 'string' && !forceAsHTML) {
return this.appendChild(document.createTextNode(content));
} else if ($.data(content, 'CWJDocument.PseudoDocumentFragmentNode')) {
while (content.hasChildNodes()) this.appendChild(content.firstChild);
return content;
} else {
if (content.constructor == CWJL.Tag) content = content.toString();
return $($(this).append(content).contents().get().pop())._setCWJDOM().get(0);
}
},
removeChildCWJ : function(node) {
$(node).remove()._setCWJDOM().get(0);
},
removeAllChildrenCWH : function() {
var nodes = $(this).contents()._setCWJDOM().get();
$(this).empty();
return nodes;
},
isDescendantOfCWJ : function(node) {
return $(node)._setBADOM().get(0).isAncestorOfCWJ(this);
},
getInnerTextCWJ : function(includeAlt) {
return $(this).text().replace(/\s+/g, ' ');
},
getAttributeCWJ : function(name) {
return $(this).attr(name);
},
setAttributeCWJ : function(name, value) {
$(this).attr(name, value);
},
hasClassNameCWJ : function(className) {
return $(this).hasClass(className);
},
appendClassNameCWJ : function(className) {
$(this).addClass(className);
},
removeClassNameCWJ : function(className) {
$(this).removeClass(className);
},
normalizeTextNodeCWJ : function(deep) {
$(this).CWJL_NormalizeText(deep);
},
getAbsoluteOffsetCWJ : function() {
var offset = $(this).offset();
return { X : offset.left, Y : offset.top };
},
getCurrentStyleCWJ : function(property, pseudo) {
return $(this).css(property);
},
setPositionFixedCWJ : function(ignoreX, ignoreY, autoHide) {
// later...
}
});
delete _CWJElement.createElementCWJ;
delete _CWJElement.createDocumentFragmentCWJ;
delete _CWJElement.getStyleSheetsCWJ;

// once called 'BARegisterDOMMethodsTo()'
var _setCWJDOM = function(node, deep) {
var valid;
try { valid = (node && (node.nodeType == 1 || node.nodeType == 11)) } catch(err) {}
if (valid && node.instanceOf != 'CWJElement') {
$.each(_CWJElement, function(p) { node[p] = _CWJElement[p] });
}
if (valid && deep === true) {
for (var i = 0, node = node.childNodes.length; i < n; i++) {
arguments.callee(node.childNodes[i], true);
}
}
return node;
};

// expose 'CWJRegisterDOMMethodsTo()' as jQuery plugin function
$.fn._setCWJDOM = function(deep) { this.each(function() { _setCWJDOM(this, deep) }); return this };

// once called 'CWJRegisterDOMMethods()'
$.each(_CWJWindow  , function(p) { window  [p] = _CWJWindow  [p] });
$.each(_CWJDocument, function(p) { document[p] = _CWJDocument[p] });
$(function() { _setCWJDOM(document.documentElement); _setCWJDOM(document.body) });

// create object references for back compatibility
var _commonDir = CWJL.GetCommonDir('common') || CWJL.GetCommonDir('shared');
CWJL.CreateBackCompat({
// deprecated 'CWJ' props
  'CWJ.url.commonDir' : _commonDir
, 'CWJ.url.imgDir'    : _commonDir + 'img/'
, 'CWJ.url.cssDir'    : _commonDir + 'css/'
, 'CWJ.url.jsDir'     : _commonDir + 'js/'
, 'CWJ.ns'            : CWJL.ns
, 'CWJ.ns.cwj'        : CWJL.ns.cwjl
, 'CWJ.env'           : CWJL.env
, 'CWJ.env.referer'   : document.referrer
, 'CWJ.ua'            : CWJL.ua
, 'CWJ.ua.revision'   : CWJL.ua.version
, 'CWJ.ua.isWinIE'    : CWJL.ua.isWin && CWJL.ua.isIE
, 'CWJ.ua.isMacIE'    : CWJL.ua.isMac && CWJL.ua.isIE
, 'CWJ.ua.isIE40'     : CWJL.ua.isIE  && CWJL.ua.version == 4.0
, 'CWJ.ua.isIE45'     : CWJL.ua.isIE  && CWJL.ua.version == 4.5
, 'CWJ.ua.isIE50'     : CWJL.ua.isIE  && CWJL.ua.version == 5.0
, 'CWJ.ua.isIE55'     : CWJL.ua.isIE  && CWJL.ua.version == 5.5
, 'CWJ.ua.isIE60'     : CWJL.ua.isIE  && CWJL.ua.version == 6.0
, 'CWJ.ua.isIE70'     : CWJL.ua.isIE  && CWJL.ua.version == 7.0
, 'CWJ.ua.isIE80'     : CWJL.ua.isIE  && CWJL.ua.version == 8.0
, 'CWJ.ua.isWinIEQM'  : CWJL.ua.isQuirksMode
, 'CWJ.ua.IEDocMode'  : CWJL.ua.documentMode

// depracated 'CWJ'-suffixed methods in build-in object
, 'Number.prototype.formatNumberCWJ' : function(format) { return CWJL.Number(this).format (format).get() }
, 'String.prototype.formatNumberCWJ' : function(format) { return CWJL.Number(this).format (format).get() }
, 'String.prototype.formatNumberCWJ' : function(format) { return CWJL.Number(this).format (format).get() }
, 'String.prototype.formatTextCWJ'   : function(arr)    { return CWJL.String(this).format    (arr).get() }
, 'String.prototype.getBeforeCWJ'    : function(str)    { return CWJL.String(this).getBefore (str).get() }
, 'String.prototype.getAfterCWJ'     : function(str)    { return CWJL.String(this).getAfter  (str).get() }
, 'String.prototype.startsWithCWJ'   : function(str)    { return CWJL.String(this).startsWith(str)       }
, 'String.prototype.endsWithCWJ'     : function(str)    { return CWJL.String(this).endsWith  (str)       }
, 'String.prototype.relToAbsCWJ'     : function(base)   { return CWJL.String(this).rel2abs  (base).get() }
, 'String.prototype.absToRelCWJ'     : function(base)   { return CWJL.String(this).abs2rel  (base).get() }
, 'String.prototype.getSanitizedStringCWJ' : function() { return CWJL.String(this).sanitize     ().get() }

// deprecated 'CWJ'-prefixed global functions
, 'CWJSingleton'            : CWJL.Singleton
, 'CWJCreateDelegate'       : CWJL.Delegate
, 'CWJAlreadyApplied'       : CWJL.AlreadyApplied
, 'CWJPreloadImage'         : CWJL.PreloadImage
, 'CWJOpenWindow'           : CWJL.OpenWindow
, 'CWJOpenFullscreenWindow' : CWJL.OpenWindow.full
, 'CWJGetGeometry'          : CWJL.GetGeometry
, 'CWJGetCommonDir'         : CWJL.GetCommonDir
, 'CWJGetStyleSheets'       : _CWJDocument.getStyleSheetsCWJ

// deprecated 'CWJ'-prefixed constructors
, 'CWJObservable'  : CWJL.Observable
, 'CWJSetTimeout'  : CWJL.Timeout
, 'CWJSetInterval' : CWJL.Interval
, 'CWJTimer'       : CWJL.Timer
, 'CWJTag'         : CWJL.Tag

// deprecated CWJDOM-related functions
, 'CWJRegisterDOMMethodsTo' : _setCWJDOM
, 'CWJAddOnload'            : function(func, aThis) { $(CWJL.Delegate(func, aThis)) }
, 'CWJAddOnunload'          : function(func, aThis) { $(window).unload(CWJL.Delegate(func, aThis)) }
, 'CWJAppendStateClassName' : function(className) { $(document.body).addClass(className) }
, 'CWJRemoveStateClassName' : function(className) { $(document.body).removeClass(className) }
, 'CWJConcatNodeList'       : function() { var arr = []; $.makeArray(arguments).forEach(function(arg) { arr.concat($.makeArray(arg)) }); return arr }
, 'CWJStartGeometryMeasure' : CWJL.GetGeometry.continuously

// deprecated ImportJS functions and properies
, 'CWJImportJS'  : CWJL.ImportJS
, 'CWJImportCSS' : CWJL.ImportCSS
});
}



/* =============== for JSDoc toolkit output =============== */
/**
 * jQuery object, contains jQuery static methods.
 * @name jQuery
 * @namespace contains jQuery methods.
 */
/**
 * jQuery instance methods.
 * @name jQuery.fn
 * @fieldOf jQuery
 * @namespace jQuery instance methods.
 */



})(jQuery);
