﻿__weblight_t_49f2cd61 = [''];



jQuery.ajaxSettings.xhr = function() {
    try {
        return window.ActiveXObject ? new ActiveXObject("Microsoft.XMLHTTP")
                || new ActiveXObject("MSXML2.XMLHTTP")
                || new ActiveXObject("MSXML2.XMLHTTP.3.0")
                : new XMLHttpRequest();
    }
    catch (e) {
        return new ActiveXObject("MSXML2.XMLHTTP");
    }
};


jQuery.parseJSON = function (data) {
    if (typeof data !== "string" || !data) {
        return null;
    }

    // original code cannot decode new Date(1270656000000) correctly

    // Make sure the incoming data is actual JSON
    // Logic borrowed from http://json.org/json2.js
    if (/^[\],:{}\s]*$/.test(data.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, "@")
    //.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, "]")
			.replace(/"[^"\\\n\r]*"|true|false|null|new\sDate\([-\d]+\)|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, "]")
			.replace(/(?:^|:|,)(?:\s*\[)+/g, ""))) {
        // Try to use the native JSON parser first
        //        return window.JSON && window.JSON.parse ?
        //				window.JSON.parse(data) :
        //				(new Function("return " + data))();
        return WebLight.decode(data);
    } else {
        jQuery.error("Invalid JSON: " + data);
    }
};

/// add jquery history plugIn 

if (jQuery && !jQuery.historyInit) {

    jQuery.extend({
        historyCurrentHash: undefined,
        historyCallback: undefined,
        historyIframeSrc: undefined,
        historyNeedIframe: jQuery.browser.msie && (jQuery.browser.version < 8 || document.documentMode < 8),

        historyInit: function(callback, src) {
            jQuery.historyCallback = callback;
            if (src) jQuery.historyIframeSrc = src;
            var current_hash = location.hash.replace(/\?.*$/, '');

            jQuery.historyCurrentHash = current_hash;
            if (jQuery.historyNeedIframe) {
                // To stop the callback firing twice during initilization if no hash present
                if (jQuery.historyCurrentHash == '') {
                    jQuery.historyCurrentHash = '#';
                }

                // add hidden iframe for IE
                jQuery("body").prepend('<iframe id="jQuery_history" style="display: none;"' +
				' src="javascript:false;"></iframe>'
			);
                var ihistory = jQuery("#jQuery_history")[0];
                var iframe = ihistory.contentWindow.document;
                iframe.open();
                iframe.close();
                iframe.location.hash = current_hash;
            }
            else if (jQuery.browser.safari) {
                // etablish back/forward stacks
                jQuery.historyBackStack = [];
                jQuery.historyBackStack.length = history.length;
                jQuery.historyForwardStack = [];
                jQuery.lastHistoryLength = history.length;

                jQuery.isFirst = true;
            }
            if (current_hash)
                jQuery.historyCallback(current_hash.replace(/^#/, ''));
            setInterval(jQuery.historyCheck, 100);
        },

        historyAddHistory: function(hash) {
            // This makes the looping function do something
            jQuery.historyBackStack.push(hash);

            jQuery.historyForwardStack.length = 0; // clear forwardStack (true click occured)
            this.isFirst = true;
        },

        historyCheck: function() {
            if (jQuery.historyNeedIframe) {
                // On IE, check for location.hash of iframe
                var ihistory = jQuery("#jQuery_history")[0];
                var iframe = ihistory.contentDocument || ihistory.contentWindow.document;
                var current_hash = iframe.location.hash.replace(/\?.*$/, '');
                if (current_hash != jQuery.historyCurrentHash) {

                    location.hash = current_hash;
                    jQuery.historyCurrentHash = current_hash;
                    jQuery.historyCallback(current_hash.replace(/^#/, ''));

                }
            } else if (jQuery.browser.safari) {
                if (jQuery.lastHistoryLength == history.length && jQuery.historyBackStack.length > jQuery.lastHistoryLength) {
                    jQuery.historyBackStack.shift();
                }
                if (!jQuery.dontCheck) {
                    var historyDelta = history.length - jQuery.historyBackStack.length;
                    jQuery.lastHistoryLength = history.length;

                    if (historyDelta) { // back or forward button has been pushed
                        jQuery.isFirst = false;
                        if (historyDelta < 0) { // back button has been pushed
                            // move items to forward stack
                            for (var i = 0; i < Math.abs(historyDelta); i++) jQuery.historyForwardStack.unshift(jQuery.historyBackStack.pop());
                        } else { // forward button has been pushed
                            // move items to back stack
                            for (var i = 0; i < historyDelta; i++) jQuery.historyBackStack.push(jQuery.historyForwardStack.shift());
                        }
                        var cachedHash = jQuery.historyBackStack[jQuery.historyBackStack.length - 1];
                        if (cachedHash != undefined) {
                            jQuery.historyCurrentHash = location.hash.replace(/\?.*$/, '');
                            jQuery.historyCallback(cachedHash);
                        }
                    } else if (jQuery.historyBackStack[jQuery.historyBackStack.length - 1] == undefined && !jQuery.isFirst) {
                        // back button has been pushed to beginning and URL already pointed to hash (e.g. a bookmark)
                        // document.URL doesn't change in Safari
                        if (location.hash) {
                            var current_hash = location.hash;
                            jQuery.historyCallback(location.hash.replace(/^#/, ''));
                        } else {
                            var current_hash = '';
                            jQuery.historyCallback('');
                        }
                        jQuery.isFirst = true;
                    }
                }
            } else {
                // otherwise, check for location.hash
                var current_hash = location.hash.replace(/\?.*$/, '');
                if (current_hash != jQuery.historyCurrentHash) {
                    jQuery.historyCurrentHash = current_hash;
                    jQuery.historyCallback(current_hash.replace(/^#/, ''));
                }
            }
        },
        historyLoad: function(hash) {
            var newhash;
            hash = decodeURIComponent(hash.replace(/\?.*$/, ''));

            if (jQuery.browser.safari) {
                newhash = hash;
            }
            else {
                newhash = '#' + hash;
                location.hash = newhash;
            }
            jQuery.historyCurrentHash = newhash;

            if (jQuery.historyNeedIframe) {
                var ihistory = jQuery("#jQuery_history")[0];
                var iframe = ihistory.contentWindow.document;
                iframe.open();
                iframe.close();
                iframe.location.hash = newhash;
                jQuery.lastHistoryLength = history.length;
                jQuery.historyCallback(hash);
            }
            else if (jQuery.browser.safari) {
                jQuery.dontCheck = true;
                // Manually keep track of the history values for Safari
                this.historyAddHistory(hash);

                // Wait a while before allowing checking so that Safari has time to update the "history" object
                // correctly (otherwise the check loop would detect a false change in hash).
                var fn = function() { jQuery.dontCheck = false; };
                window.setTimeout(fn, 200);
                jQuery.historyCallback(hash);
                // N.B. "location.hash=" must be the last line of code for Safari as execution stops afterwards.
                //      By explicitly using the "location.hash" command (instead of using a variable set to "location.hash") the
                //      URL in the browser and the "history" object are both updated correctly.
                location.hash = newhash;
            }
            else {
                jQuery.historyCallback(hash);
            }
        }
    });

}
/// <reference path="../jquery/jquery-1.4.1.js" />
/// <reference path="ext-jquery-adapter.js" />
/// <reference path="ext-all.js" />


Ext.grid.RowSelectionModel.override({
    getSelectedIndex: function() {
        return this.grid.store.indexOf(this.selections.itemAt(0));
    }
});

Ext.override(Ext.form.Field, {
    setFieldLabel: function(text) {
        if (this.rendered) {
            this.el.up('.x-form-item', 10, true).child('.x-form-item-label').update(text);
        }
        this.fieldLabel = text;
    }
});

/// fix ie bug
/// if we add custom style to form panel and add padding left or padding right to x-form-item
/// the default   innerCt.setSize(adjWidth, adjHeight); did not calculate the value, and put the actual width on it, so it will break to new line.
/// for now ,change to use percentage for temp fix.
Ext.override(Ext.form.CompositeField, {
    onResize: function (adjWidth, adjHeight, rawWidth, rawHeight) {
        var innerCt = this.innerCt;
        if (innerCt) {
            if (this.rendered && innerCt.rendered) {
                innerCt.setSize(adjWidth, adjHeight);
            }
            innerCt.setWidth("98%");
        }
        Ext.form.CompositeField.superclass.onResize.apply(this, arguments);
    }
});
/*
/// for WCF, use Microsoft Json date
/// represents the number of milliseconds since January 1, 1970 in Universal Coordinated Time (UTC).
Ext.util.JSON.encodeDate = function(o) {
    if (null == o)
        return null;
    else
        return '"/Date(' + o.getTime() + '-0500)/"';
};

Ext.data.Field.prototype.dateFormat = 'wcf';

Date.parseDate = function(input, format, strict) {
    if (format == 'wcf') {
        var msdateRe = /\/Date\((\d+)((\+|\-)\d+)?\)\// // MS JSON date format

        if (input.match(msdateRe).length > 1) {
            // expects MS JSON dates of the form \/Date(1069689066000)\/
            return new Date(parseInt(input.replace(msdateRe, '$1')));
        } 
    }

    var p = Date.parseFunctions;
    if (p[format] == null) {
        Date.createParser(format);
    }
    return p[format](input, Ext.isDefined(strict) ? strict : Date.useStrict);
};
*/

/*
 * jQuery history plugin
 * 
 * sample page: http://www.mikage.to/jquery/jquery_history.html
 *
 * Copyright (c) 2006-2009 Taku Sano (Mikage Sawatari)
 * Licensed under the MIT License:
 *   http://www.opensource.org/licenses/mit-license.php
 *
 * Modified by Lincoln Cooper to add Safari support and only call the callback once during initialization
 * for msie when no initial hash supplied.
 */


jQuery.extend({
	historyCurrentHash: undefined,
	historyCallback: undefined,
	historyIframeSrc: undefined,
	historyNeedIframe: jQuery.browser.msie && (jQuery.browser.version < 8 || document.documentMode < 8),
	
	historyInit: function(callback, src){
		jQuery.historyCallback = callback;
		if (src) jQuery.historyIframeSrc = src;
		var current_hash = location.hash.replace(/\?.*$/, '');
		
		jQuery.historyCurrentHash = current_hash;
		if (jQuery.historyNeedIframe) {
			// To stop the callback firing twice during initilization if no hash present
			if (jQuery.historyCurrentHash == '') {
				jQuery.historyCurrentHash = '#';
			}
		
			// add hidden iframe for IE
			jQuery("body").prepend('<iframe id="jQuery_history" style="display: none;"'+
				' src="javascript:false;"></iframe>'
			);
			var ihistory = jQuery("#jQuery_history")[0];
			var iframe = ihistory.contentWindow.document;
			iframe.open();
			iframe.close();
			iframe.location.hash = current_hash;
		}
		else if (jQuery.browser.safari) {
			// etablish back/forward stacks
			jQuery.historyBackStack = [];
			jQuery.historyBackStack.length = history.length;
			jQuery.historyForwardStack = [];
			jQuery.lastHistoryLength = history.length;
			
			jQuery.isFirst = true;
		}
		if(current_hash)
			jQuery.historyCallback(current_hash.replace(/^#/, ''));
		setInterval(jQuery.historyCheck, 100);
	},
	
	historyAddHistory: function(hash) {
		// This makes the looping function do something
		jQuery.historyBackStack.push(hash);
		
		jQuery.historyForwardStack.length = 0; // clear forwardStack (true click occured)
		this.isFirst = true;
	},
	
	historyCheck: function(){
		if (jQuery.historyNeedIframe) {
			// On IE, check for location.hash of iframe
			var ihistory = jQuery("#jQuery_history")[0];
			var iframe = ihistory.contentDocument || ihistory.contentWindow.document;
			var current_hash = iframe.location.hash.replace(/\?.*$/, '');
			if(current_hash != jQuery.historyCurrentHash) {
			
				location.hash = current_hash;
				jQuery.historyCurrentHash = current_hash;
				jQuery.historyCallback(current_hash.replace(/^#/, ''));
				
			}
		} else if (jQuery.browser.safari) {
			if(jQuery.lastHistoryLength == history.length && jQuery.historyBackStack.length > jQuery.lastHistoryLength) {
				jQuery.historyBackStack.shift();
			}
			if (!jQuery.dontCheck) {
				var historyDelta = history.length - jQuery.historyBackStack.length;
				jQuery.lastHistoryLength = history.length;
				
				if (historyDelta) { // back or forward button has been pushed
					jQuery.isFirst = false;
					if (historyDelta < 0) { // back button has been pushed
						// move items to forward stack
						for (var i = 0; i < Math.abs(historyDelta); i++) jQuery.historyForwardStack.unshift(jQuery.historyBackStack.pop());
					} else { // forward button has been pushed
						// move items to back stack
						for (var i = 0; i < historyDelta; i++) jQuery.historyBackStack.push(jQuery.historyForwardStack.shift());
					}
					var cachedHash = jQuery.historyBackStack[jQuery.historyBackStack.length - 1];
					if (cachedHash != undefined) {
						jQuery.historyCurrentHash = location.hash.replace(/\?.*$/, '');
						jQuery.historyCallback(cachedHash);
					}
				} else if (jQuery.historyBackStack[jQuery.historyBackStack.length - 1] == undefined && !jQuery.isFirst) {
					// back button has been pushed to beginning and URL already pointed to hash (e.g. a bookmark)
					// document.URL doesn't change in Safari
					if (location.hash) {
						var current_hash = location.hash;
						jQuery.historyCallback(location.hash.replace(/^#/, ''));
					} else {
						var current_hash = '';
						jQuery.historyCallback('');
					}
					jQuery.isFirst = true;
				}
			}
		} else {
			// otherwise, check for location.hash
			var current_hash = location.hash.replace(/\?.*$/, '');
			if(current_hash != jQuery.historyCurrentHash) {
				jQuery.historyCurrentHash = current_hash;
				jQuery.historyCallback(current_hash.replace(/^#/, ''));
			}
		}
	},
	historyLoad: function(hash){
		var newhash;
		hash = decodeURIComponent(hash.replace(/\?.*$/, ''));
		
		if (jQuery.browser.safari) {
			newhash = hash;
		}
		else {
			newhash = '#' + hash;
			location.hash = newhash;
		}
		jQuery.historyCurrentHash = newhash;
		
		if (jQuery.historyNeedIframe) {
			var ihistory = jQuery("#jQuery_history")[0];
			var iframe = ihistory.contentWindow.document;
			iframe.open();
			iframe.close();
			iframe.location.hash = newhash;
			jQuery.lastHistoryLength = history.length;
			jQuery.historyCallback(hash);
		}
		else if (jQuery.browser.safari) {
			jQuery.dontCheck = true;
			// Manually keep track of the history values for Safari
			this.historyAddHistory(hash);
			
			// Wait a while before allowing checking so that Safari has time to update the "history" object
			// correctly (otherwise the check loop would detect a false change in hash).
			var fn = function() {jQuery.dontCheck = false;};
			window.setTimeout(fn, 200);
			jQuery.historyCallback(hash);
			// N.B. "location.hash=" must be the last line of code for Safari as execution stops afterwards.
			//      By explicitly using the "location.hash" command (instead of using a variable set to "location.hash") the
			//      URL in the browser and the "history" object are both updated correctly.
			location.hash = newhash;
		}
		else {
		  jQuery.historyCallback(hash);
		}
	}
});



/**
 * Cookie plugin
 *
 * Copyright (c) 2006 Klaus Hartl (stilbuero.de)
 * Dual licensed under the MIT and GPL licenses:
 * http://www.opensource.org/licenses/mit-license.php
 * http://www.gnu.org/licenses/gpl.html
 *
 */

/**
 * Create a cookie with the given name and value and other optional parameters.
 *
 * @example $.cookie('the_cookie', 'the_value');
 * @desc Set the value of a cookie.
 * @example $.cookie('the_cookie', 'the_value', { expires: 7, path: '/', domain: 'jquery.com', secure: true });
 * @desc Create a cookie with all available options.
 * @example $.cookie('the_cookie', 'the_value');
 * @desc Create a session cookie.
 * @example $.cookie('the_cookie', null);
 * @desc Delete a cookie by passing null as value. Keep in mind that you have to use the same path and domain
 *       used when the cookie was set.
 *
 * @param String name The name of the cookie.
 * @param String value The value of the cookie.
 * @param Object options An object literal containing key/value pairs to provide optional cookie attributes.
 * @option Number|Date expires Either an integer specifying the expiration date from now on in days or a Date object.
 *                             If a negative value is specified (e.g. a date in the past), the cookie will be deleted.
 *                             If set to null or omitted, the cookie will be a session cookie and will not be retained
 *                             when the the browser exits.
 * @option String path The value of the path atribute of the cookie (default: path of page that created the cookie).
 * @option String domain The value of the domain attribute of the cookie (default: domain of page that created the cookie).
 * @option Boolean secure If true, the secure attribute of the cookie will be set and the cookie transmission will
 *                        require a secure protocol (like HTTPS).
 * @type undefined
 *
 * @name $.cookie
 * @cat Plugins/Cookie
 * @author Klaus Hartl/klaus.hartl@stilbuero.de
 */

/**
 * Get the value of a cookie with the given name.
 *
 * @example $.cookie('the_cookie');
 * @desc Get the value of a cookie.
 *
 * @param String name The name of the cookie.
 * @return The value of the cookie.
 * @type String
 *
 * @name $.cookie
 * @cat Plugins/Cookie
 * @author Klaus Hartl/klaus.hartl@stilbuero.de
 */
jQuery.cookie = function(name, value, options) {
    if (typeof value != 'undefined') { // name and value given, set cookie
        options = options || {};
        if (value === null) {
            value = '';
            options = $.extend({}, options); // clone object since it's unexpected behavior if the expired property were changed
            options.expires = -1;
        }
        var expires = '';
        if (options.expires && (typeof options.expires == 'number' || options.expires.toUTCString)) {
            var date;
            if (typeof options.expires == 'number') {
                date = new Date();
                date.setTime(date.getTime() + (options.expires * 24 * 60 * 60 * 1000));
            } else {
                date = options.expires;
            }
            expires = '; expires=' + date.toUTCString(); // use expires attribute, max-age is not supported by IE
        }
        // NOTE Needed to parenthesize options.path and options.domain
        // in the following expressions, otherwise they evaluate to undefined
        // in the packed version for some reason...
        var path = options.path ? '; path=' + (options.path) : '';
        var domain = options.domain ? '; domain=' + (options.domain) : '';
        var secure = options.secure ? '; secure' : '';
        document.cookie = [name, '=', encodeURIComponent(value), expires, path, domain, secure].join('');
    } else { // only name given, get cookie
        var cookieValue = null;
        if (document.cookie && document.cookie != '') {
            var cookies = document.cookie.split(';');
            for (var i = 0; i < cookies.length; i++) {
                var cookie = jQuery.trim(cookies[i]);
                // Does this cookie string begin with the name we want?
                if (cookie.substring(0, name.length + 1) == (name + '=')) {
                    cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                    break;
                }
            }
        }
        return cookieValue;
    }
};
/// <reference path="../references/jquery-1.4.1.js" />

WebLight = { version: '1.10.0909' };

//Ext.lib.Ajax.defaultPostHeader = null;
jQuery.ajaxSettings.contentType = 'application/json; charset=UTF-8';

// same as c# String.Format method
String.format = function() {
    var s = arguments[0];
    for (var i = 0; i < arguments.length - 1; i++) {
        var reg = new RegExp("\\{" + i + "\\}", "gm");
        s = s.replace(reg, arguments[i + 1]);
    }
    return s;
}

String.prototype.startsWith = function(str) { return (this.match("^" + str) == str) }
String.prototype.endsWith = function(str) { return (this.match(str + "$") == str) }
String.prototype.trim = function() { return this.replace(/^\s+|\s+$/, ''); };

// Array.indexOf( value, begin, strict ) - Return index of the first element that matches value
Array.prototype.indexOf = function (v, b, s) {
    for (var i = +b || 0, l = this.length; i < l; i++) {
        if (this[i] === v || s && this[i] == v) { return i; }
    }
    return -1;
};

// Array.unique( strict ) - Remove duplicate values
Array.prototype.unique = function (b) {
    var a = [], i, l = this.length;
    for (i = 0; i < l; i++) {
        if (a.indexOf(this[i], 0, b) < 0) { a.push(this[i]); }
    }
    return a;
};


/**
* Formats the number according to the 'format' string; 
* adherses to the american number standard where a comma 
* is inserted after every 3 digits.
*  note: there should be only 1 contiguous number in the format, 
* where a number consists of digits, period, and commas
*        any other characters can be wrapped around this number, including '$', '%', or text
*        examples (123456.789):
*          '0′ - (123456) show only digits, no precision
*          '0.00′ - (123456.78) show only digits, 2 precision
*          '0.0000′ - (123456.7890) show only digits, 4 precision
*          '0,000′ - (123,456) show comma and digits, no precision
*          '0,000.00′ - (123,456.78) show comma and digits, 2 precision
*          '0,0.00′ - (123,456.78) shortcut method, show comma and digits, 2 precision
*
* @method format
* @param format {string} the way you would like to format this text
* @return {string} the formatted number
* @public
*/

Number.prototype.format = function(format) {
    if (!isType(format, 'string')) { return ''; } // sanity check 

    var hasComma = -1 < format.indexOf(','),
    psplit = format.stripNonNumeric().split('.'),
    that = this;

    // compute precision
    if (1 < psplit.length) {
        // fix number precision
        that = that.toFixed(psplit[1].length);
    }
    // error: too many periods
    else if (2 < psplit.length) {
        throw ('NumberFormatException: invalid format, formats should have no more than 1 period: ' + format);
    }
    // remove precision
    else {
        that = that.toFixed(0);
    }

    // get the string now that precision is correct
    var fnum = that.toString();

    // format has comma, then compute commas
    if (hasComma) {
        // remove precision for computation
        psplit = fnum.split('.');

        var cnum = psplit[0],
      parr = [],
      j = cnum.length,
      m = Math.floor(j / 3),
      n = cnum.length % 3 || 3; // n cannot be ZERO or causes infinite loop 

        // break the number into chunks of 3 digits; first chunk may be less than 3
        for (var i = 0; i < j; i += n) {
            if (i != 0) { n = 3; }
            parr[parr.length] = cnum.substr(i, n);
            m -= 1;
        }

        // put chunks back together, separated by comma
        fnum = parr.join(',');

        // add the precision back in
        if (psplit[1]) { fnum += '.' + psplit[1]; }
    }

    // replace the number portion of the format with fnum
    return format.replace(/[\d,?\.?]+/, fnum);
};

// for old browsers
window["undefined"] = window["undefined"];

WebLight.apply = function(o, c, defaults) {
    if (defaults) {
        // no "this" reference for friendly out of scope calls
        WebLight.apply(o, defaults);
    }
    if (o && c && typeof c == 'object') {
        for (var p in c) {
            o[p] = c[p];
        }
    }
    return o;
};

(function() {

    var idSeed = 0,
        toString = Object.prototype.toString,
        ua = navigator.userAgent.toLowerCase(),
        check = function (r) {
            return r.test(ua);
        },
        DOC = document,
        isStrict = DOC.compatMode == "CSS1Compat",
        isOpera = check(/opera/),
        isChrome = check(/\bchrome\b/),
        isWebKit = check(/webkit/),
        isSafari = !isChrome && check(/safari/),
        isSafari2 = isSafari && check(/applewebkit\/4/), // unique to Safari 2
        isSafari3 = isSafari && check(/version\/3/),
        isSafari4 = isSafari && check(/version\/4/),
        isIE = !isOpera && check(/msie/),
        isIE7 = isIE && check(/msie 7/),
        isIE8 = isIE && check(/msie 8/),
        isIE6 = isIE && !isIE7 && !isIE8,
        isGecko = !isWebKit && check(/gecko/),
        isGecko2 = isGecko && check(/rv:1\.8/),
        isGecko3 = isGecko && check(/rv:1\.9/),
        isBorderBox = isIE && !isStrict,
        isWindows = check(/windows|win32/),
        isMac = check(/macintosh|mac os x/),
        isAir = check(/adobeair/),
        isLinux = check(/linux/),
        isSecure = /^https/i.test(window.location.protocol);

    WebLight.apply(WebLight, {
        namespace: function () {
            var a = arguments, o = null, i, j, d, rt;
            for (i = 0; i < a.length; ++i) {
                d = a[i].split(".");
                rt = d[0];
                eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
                for (j = 1; j < d.length; ++j) {
                    o[d[j]] = o[d[j]] || {};
                    o = o[d[j]];
                }
            }
        },
        /**
        * Indicates whether to use native browser parsing for JSON methods.
        * This option is ignored if the browser does not support native JSON methods.
        * <b>Note: Native JSON methods will not work with objects that have functions.
        * Also, property names must be quoted, otherwise the data will not parse.</b> (Defaults to false)
        * @type Boolean
        */
        USE_NATIVE_JSON: false,
        /**
        * Copies all the properties of config to obj if they don't already exist.
        * @param {Object} obj The receiver of the properties
        * @param {Object} config The source of the properties
        * @return {Object} returns obj
        */
        applyIf: function (o, c) {
            if (o) {
                for (var p in c) {
                    if (!WebLight.isDefined(o[p])) {
                        o[p] = c[p];
                    }
                }
            }
            return o;
        },
        /**
        * <p>Returns true if the passed value is empty.</p>
        * <p>The value is deemed to be empty if it is<div class="mdetail-params"><ul>
        * <li>null</li>
        * <li>undefined</li>
        * <li>an empty array</li>
        * <li>a zero length string (Unless the <tt>allowBlank</tt> parameter is <tt>true</tt>)</li>
        * </ul></div>
        * @param {Mixed} value The value to test
        * @param {Boolean} allowBlank (optional) true to allow empty strings (defaults to false)
        * @return {Boolean}
        */
        isEmpty: function (v, allowBlank) {
            return v === null || v === undefined || ((WebLight.isArray(v) && !v.length)) || (!allowBlank ? v === '' : false);
        },

        /**
        * Returns true if the passed value is a JavaScript array, otherwise false.
        * @param {Mixed} value The value to test
        * @return {Boolean}
        */
        isArray: function (v) {
            return toString.apply(v) === '[object Array]';
        },

        /**
        * Returns true if the passed object is a JavaScript date object, otherwise false.
        * @param {Object} object The object to test
        * @return {Boolean}
        */
        isDate: function (v) {
            return toString.apply(v) === '[object Date]';
        },

        /**
        * Returns true if the passed value is a JavaScript Object, otherwise false.
        * @param {Mixed} value The value to test
        * @return {Boolean}
        */
        isObject: function (v) {
            return !!v && Object.prototype.toString.call(v) === '[object Object]';
        },

        /**
        * Returns true if the passed value is a JavaScript 'primitive', a string, number or boolean.
        * @param {Mixed} value The value to test
        * @return {Boolean}
        */
        isPrimitive: function (v) {
            return WebLight.isString(v) || WebLight.isNumber(v) || WebLight.isBoolean(v);
        },

        /**
        * Returns true if the passed value is a JavaScript Function, otherwise false.
        * @param {Mixed} value The value to test
        * @return {Boolean}
        */
        isFunction: function (v) {
            return toString.apply(v) === '[object Function]';
        },

        /**
        * Returns true if the passed value is a number. Returns false for non-finite numbers.
        * @param {Mixed} value The value to test
        * @return {Boolean}
        */
        isNumber: function (v) {
            return typeof v === 'number' && isFinite(v);
        },

        /**
        * Returns true if the passed value is a string.
        * @param {Mixed} value The value to test
        * @return {Boolean}
        */
        isString: function (v) {
            return typeof v === 'string';
        },

        /**
        * Returns true if the passed value is a boolean.
        * @param {Mixed} value The value to test
        * @return {Boolean}
        */
        isBoolean: function (v) {
            return typeof v === 'boolean';
        },

        /**
        * Returns true if the passed value is an HTMLElement
        * @param {Mixed} value The value to test
        * @return {Boolean}
        */
        isElement: function (v) {
            return !!v && v.tagName;
        },

        /**
        * Returns true if the passed value is not undefined.
        * @param {Mixed} value The value to test
        * @return {Boolean}
        */
        isDefined: function (v) {
            return typeof v !== 'undefined';
        },
        wcfPost: function (url, data, success, error, beforeSend) {
            return $.ajax({
                url: url,
                type: 'POST',
                data: data,
                dataType: 'json',
                contentType: "application/json; charset=utf-8",
                error: error,
                beforeSend: beforeSend,
                success: success
            });
        },

        post: function( url, data, success, error ) {
		    // shift arguments if data argument was omited
		    if ( jQuery.isFunction( data ) ) {
			    error = error || success;
			    success = data;
			    data = {};
		    }

            /// for web.config security control purpose
            /// must add prameter, then it will use weblight.aspx rule
            //url = Ext.urlAppend(url, '_dc=' + (new Date().getTime()) + '.gif');

		    return jQuery.ajax({
			    type: "POST",
			    url: url,
			    data: data,
			    success: success,
                error:error,
			    dataType: 'json',
                contentType: 'application/json; charset=utf-8'
		    });
	    },

        /**
        * <p>Extends one class to create a subclass and optionally overrides members with the passed literal. This method
        * also adds the function "override()" to the subclass that can be used to override members of the class.</p>
        * For example, to create a subclass of Ext GridPanel:
        * <pre><code>
        MyGridPanel = Ext.extend(Ext.grid.GridPanel, {
        constructor: function(config) {

        //      Create configuration for this Grid.
        var store = new Ext.data.Store({...});
        var colModel = new Ext.grid.ColumnModel({...});

        //      Create a new config object containing our computed properties
        //      *plus* whatever was in the config parameter.
        config = Ext.apply({
        store: store,
        colModel: colModel
        }, config);

        MyGridPanel.superclass.constructor.call(this, config);

        //      Your postprocessing here
        },

        yourMethod: function() {
        // etc.
        }
        });
        </code></pre>
        *
        * <p>This function also supports a 3-argument call in which the subclass's constructor is
        * passed as an argument. In this form, the parameters are as follows:</p>
        * <div class="mdetail-params"><ul>
        * <li><code>subclass</code> : Function <div class="sub-desc">The subclass constructor.</div></li>
        * <li><code>superclass</code> : Function <div class="sub-desc">The constructor of class being extended</div></li>
        * <li><code>overrides</code> : Object <div class="sub-desc">A literal with members which are copied into the subclass's
        * prototype, and are therefore shared among all instances of the new class.</div></li>
        * </ul></div>
        *
        * @param {Function} superclass The constructor of class being extended.
        * @param {Object} overrides <p>A literal with members which are copied into the subclass's
        * prototype, and are therefore shared between all instances of the new class.</p>
        * <p>This may contain a special member named <tt><b>constructor</b></tt>. This is used
        * to define the constructor of the new class, and is returned. If this property is
        * <i>not</i> specified, a constructor is generated and returned which just calls the
        * superclass's constructor passing on its parameters.</p>
        * <p><b>It is essential that you call the superclass constructor in any provided constructor. See example code.</b></p>
        * @return {Function} The subclass constructor from the <code>overrides</code> parameter, or a generated one if not provided.
        */
        extend: function () {
            // inline overrides
            var io = function (o) {
                for (var m in o) {
                    this[m] = o[m];
                }
            };
            var oc = Object.prototype.constructor;

            return function (sb, sp, overrides) {
                if (WebLight.isObject(sp)) {
                    overrides = sp;
                    sp = sb;
                    sb = overrides.constructor != oc ? overrides.constructor : function () { sp.apply(this, arguments); };
                }
                var F = function () { },
                    sbp,
                    spp = sp.prototype;

                F.prototype = spp;
                sbp = sb.prototype = new F();
                sbp.constructor = sb;
                sb.superclass = spp;
                if (spp.constructor == oc) {
                    spp.constructor = sp;
                }
                sb.override = function (o) {
                    WebLight.override(sb, o);
                };
                sbp.superclass = sbp.supr = (function () {
                    return spp;
                });
                sbp.override = io;
                WebLight.override(sb, overrides);
                sb.extend = function (o) { return WebLight.extend(sb, o); };
                return sb;
            };
        } (),
        /**
        * Converts any iterable (numeric indices and a length property) into a true array
        * Don't use this on strings. IE doesn't support "abc"[0] which this implementation depends on.
        * For strings, use this instead: "abc".match(/./g) => [a,b,c];
        * @param {Iterable} the iterable object to be turned into a true Array.
        * @return (Array) array
        */
        toArray: function () {
            return isIE ?
                 function (a, i, j, res) {
                     res = [];
                     for (var x = 0, len = a.length; x < len; x++) {
                         res.push(a[x]);
                     }
                     return res.slice(i || 0, j || res.length);
                 } :
                 function (a, i, j) {
                     return Array.prototype.slice.call(a, i || 0, j || a.length);
                 }
        } (),
        isIterable: function (v) {
            //check for array or arguments
            if (WebLight.isArray(v) || v.callee) {
                return true;
            }
            //check for node list type
            if (/NodeList|HTMLCollection/.test(toString.call(v))) {
                return true;
            }
            //NodeList has an item and length property
            //IXMLDOMNodeList has nextNode method, needs to be checked first.
            return ((typeof v.nextNode != 'undefined' || v.item) && WebLight.isNumber(v.length));
        },
        /**
        * Iterates an array calling the supplied function.
        * @param {Array/NodeList/Mixed} array The array to be iterated. If this
        * argument is not really an array, the supplied function is called once.
        * @param {Function} fn The function to be called with each item. If the
        * supplied function returns false, iteration stops and this method returns
        * the current <code>index</code>. This function is called with
        * the following arguments:
        * <div class="mdetail-params"><ul>
        * <li><code>item</code> : <i>Mixed</i>
        * <div class="sub-desc">The item at the current <code>index</code>
        * in the passed <code>array</code></div></li>
        * <li><code>index</code> : <i>Number</i>
        * <div class="sub-desc">The current index within the array</div></li>
        * <li><code>allItems</code> : <i>Array</i>
        * <div class="sub-desc">The <code>array</code> passed as the first
        * argument to <code>Ext.each</code>.</div></li>
        * </ul></div>
        * @param {Object} scope The scope (<code>this</code> reference) in which the specified function is executed.
        * Defaults to the <code>item</code> at the current <code>index</code>
        * within the passed <code>array</code>.
        * @return See description for the fn parameter.
        */
        each: function (array, fn, scope) {
            if (WebLight.isEmpty(array, true)) {
                return;
            }
            if (!WebLight.isIterable(array) || WebLight.isPrimitive(array)) {
                array = [array];
            }
            for (var i = 0, len = array.length; i < len; i++) {
                if (fn.call(scope || array[i], array[i], i, array) === false) {
                    return i;
                };
            }
        },
        /**         
        * Escapes the passed string for use in a regular expression         
        * @param {String} str         
        * @return {String}         
        */
        escapeRe: function (s) {
            return s.replace(/([-.*+?^${}()|[\]\/\\])/g, "\\$1");
        },
        /**
        * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
        * Usage:<pre><code>
        Ext.override(MyClass, {
        newMethod1: function(){
        // etc.
        },
        newMethod2: function(foo){
        // etc.
        }
        });
        </code></pre>
        * @param {Object} origclass The class to override
        * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
        * containing one or more methods.
        * @method override
        */
        override: function (origclass, overrides) {
            if (overrides) {
                var p = origclass.prototype;
                WebLight.apply(p, overrides);
                if (WebLight.isIE && overrides.hasOwnProperty('toString')) {
                    p.toString = overrides.toString;
                }
            }
        },
        // obsolete method
        getProxyUrl: function (sysName, action) {
            return String.format("/weblight.aspx/{0}/{1}", sysName, action);
        },

        getMethodUrl: function(sysName,action) {
            return String.format("/weblight.aspx/{0}/{1}", sysName, action);
        },

        newGuid: function () {
            // http://www.ietf.org/rfc/rfc4122.txt 
            /* approach 1
            var s = []; 
            var hexDigits = "0123456789ABCDEF"; 
            for (var i = 0; i < 32; i++) { 
            s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1); 
            } 
            s[12] = "4";  // bits 12-15 of the time_hi_and_version field to 0010 
            s[16] = hexDigits.substr((s[16] & 0x3) | 0x8, 1);  // bits 6-7 of the clock_seq_hi_and_reserved to 01 
 
            var uuid = s.join(""); 
            return uuid; 
            */

            /// approach 2
            return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
                var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
                return v.toString(16);
            }).toUpperCase();

        },

        clone: function (o) {
            if (!o || 'object' !== typeof o) {
                return o;
            }
            if ('function' === typeof o.clone) {
                return o.clone();
            }
            var c = '[object Array]' === Object.prototype.toString.call(o) ? [] : {};
            var p, v;
            for (p in o) {
                if (o.hasOwnProperty(p)) {
                    v = o[p];
                    if (v && 'object' === typeof v) {
                        c[p] = WebLight.clone(v);
                    }
                    else {
                        c[p] = v;
                    }
                }
            }
            return c;
        },
        urlEncodeCharacter: function(c)
        {
	        return '%' + c.charCodeAt(0).toString(16);
        },

        urlDecodeCharacter : function(str, c)
        {
	        return String.fromCharCode(parseInt(c, 16));
        },

        urlEncode : function( s )
        {
              return encodeURIComponent( s ).replace( /\%20/g, '+' ).replace( /[!'()*~]/g, WebLight.urlEncodeCharacter );
        },

        urlDecode : function( s )
        {
              return decodeURIComponent(s.replace( /\+/g, '%20' )).replace( /\%([0-9a-f]{2})/g, WebLight.urlDecodeCharacter);
        }
    });
})();

WebLight.override(RegExp, {
	/**
	 * Escapes regular expression
	 * @param {String} s
	 * @return {String} The escaped string
	 * @static
	 */
	escape:function(s) {
		if('string' !== typeof s) {
			return s;
		}
		return s.replace(/([.*+?\^=!:${}()|\[\]\/\\])/g, '\\$1');
	} // eo function escape

	/**
	 * Clones RegExp object
	 * @return {RegExp} Clone of this RegExp
	 */
	,clone:function() {
		return new RegExp(this);
	} // eo function clone
});

// Array
// {{{
WebLight.override(Array, {
	// {{{
	/**
	 * One dimensional copy. Use {@link Ext.ux.util#clone Ext.ux.util.clone} to deeply clone an Array.
	 * @member Array
	 * @return {Array} New Array that is copy of this
	 */
	 copy:function() {
		var a = [];
		for(var i = 0, l = this.length; i < l; i++) {
			a.push(this[i]);
		}
		return a;
	} // eo function copy
	// }}}
	// {{{
	/**
	 * Not used anyway as Ext has its own indexOf
	 * @member Array
	 * @return {Integer} Index of v or -1 if not found
	 * @param {Mixed} v Value to find indexOf
	 * @param {Integer} b Starting index
	 */
	,indexOf:function(v, b) {
		for(var i = +b || 0, l = this.length; i < l; i++) {
			if(this[i] === v) { 
				return i; 
			}
		}
		return -1;
	} // eo function indexOf
	// }}}
	// {{{
	/**
	 * Returns intersection of this Array and passed arguments
	 * @member Array
	 * @return {Array} Intersection of this and passed arguments
	 * @param {Mixed} arg1 (optional)
	 * @param {Mixed} arg2 (optional)
	 * @param {Mixed} etc. (optional)
	 */
	,intersect:function() {
		if(!arguments.length) {
			return [];
		}
		var a1 = this, a2, a;
		for(var k = 0, ac = arguments.length; k < ac; k++) {
			a = [];
			a2 = arguments[k] || [];
			for(var i = 0, l = a1.length; i < l; i++) {
				if(-1 < a2.indexOf(a1[i])) {
					a.push(a1[i]);
				}
			}
			a1 = a;
		}
		return a.unique();
	} // eo function intesect
	// }}}
	// {{{
	/**
	 * Returns last index of passed argument
	 * @member Array
	 * @return {Integer} Index of v or -1 if not found
	 * @param {Mixed} v Value to find indexOf
	 * @param {Integer} b Starting index
	 */
	,lastIndexOf:function(v, b) {
		b = +b || 0;
		var i = this.length; 
		while(i-- > b) {
			if(this[i] === v) { 
				return i; 
			}
		}
		return -1;
	} // eof function lastIndexOf
	// }}}
	// {{{
	/**
	 * @member Array
	 * @return {Array} New Array that is union of this and passed arguments
	 * @param {Mixed} arg1 (optional)
	 * @param {Mixed} arg2 (optional)
	 * @param {Mixed} etc. (optional)
	 */
	,union:function() {
		var a = this.copy(), a1;
		for(var k = 0, ac = arguments.length; k < ac; k++) {
			a1 = arguments[k] || [];
			for(var i = 0, l = a1.length; i < l; i++) {
				a.push(a1[i]);
			}
		}
		return a.unique();
	} // eo function union
	// }}}
	// {{{
	/**
	 * Removes duplicates from array
	 * @member Array
	 * @return {Array} New Array with duplicates removed
	 */
	,unique:function() {
		var a = [], i, l = this.length;
		for(i = 0; i < l; i++) {
			if(a.indexOf(this[i]) < 0) { 
				a.push(this[i]); 
			}
		}
		return a;
	} // eo function unique
	// }}}

});
// }}}

/* Fix for Opera, which does not seem to include the map function on Array's */
if (!Array.prototype.map) {
    Array.prototype.map = function(fun){
        var len = this.length;
        if (typeof fun != 'function') {
            throw new TypeError();
        }
        var res = new Array(len);
        var thisp = arguments[1];
        for (var i = 0; i < len; i++) {
            if (i in this) {
                res[i] = fun.call(thisp, this[i], i, this);
            }
        }
        return res;
    };
}

WebLight.namespace('WebLight.data', 'WebLight.form','WebLight.grid','WebLight.object','WebLight.util');

/// <reference path="../references/jquery-1.4.1.js" />
/// <reference path="../references/ext-jquery-adapter.js" />
/// <reference path="../references/ext-all.js" />


WebLight.util.MixedCollection = Ext.util.MixedCollection;
WebLight.util.Json = Ext.util.JSON;
WebLight.util.Observable = Ext.util.Observable;
WebLight.util.DelayedTask = Ext.util.DelayedTask;
WebLight.data.SortTypes = Ext.data.SortTypes;
WebLight.data.Field = Ext.data.Field;
WebLight.data.Record = Ext.data.Record;
WebLight.encode = Ext.util.JSON.encode;
WebLight.decode = Ext.util.JSON.decode;
/// <reference path="../references/jquery-1.4.1.js" />
/// <reference path="../Core.js" />
/// <reference path="../util/DelayedTask.js" />
/// <reference path="../util/Observable.js" />

/// query builder
WebLight.data.Queryable = WebLight.extend(WebLight.util.Observable, {

    /// json store
    store: null,

    constructor: function (config) {
        WebLight.apply(this, config);
        WebLight.data.Queryable.superclass.constructor.call(this, config);
        /// inner params cache 
        this.params = {};
    },
    /// set startIndex
    skip: function (count) {
        /// if store defined and is jsonstore object
        if (this.store && WebLight.isDefined(this.store.setBaseParam))
            this.store.setBaseParam(this.store.paramNames.start, count);
        this.params.start = count;
        return this;
    },

    /// set page size
    take: function (count) {
        /// if store defined and is jsonstore object
        if (this.store && WebLight.isDefined(this.store.setBaseParam))
            this.store.setBaseParam(this.store.paramNames.limit, count);
        this.params.limit = count;
        return this;
    },
    /// set order field and direction
    orderBy: function (ordering) {
        var orders = ordering.split(' ');
        this.params.sort = orders[0];
        this.params.dir = orders.length == 1 ? 'ASC' : orders[1];
        if (this.store && WebLight.isDefined(this.store.setBaseParam)) {
            this.store.setBaseParam(this.store.paramNames.sort, orders[0]);
            this.store.setBaseParam(this.store.paramNames.dir, orders.length == 1 ? 'ASC' : orders[1]);
        }
        return this;
    },
    /* add filters
    syntax:
    if predicate is null or empty or not start with && ,then clear filters
    else append filters
    operators:
    == equal
    >= great than or equal
    <= less than or equal
    != not equal
    ^= start with
    ^! not start with
    > great than
    < less than
    */
    where: function (predicate, values) {

        // format predicate if values defined
        if (arguments.length == 0 || !predicate || predicate.trim() == '') {
            this.lastPredicate = '';
            this.params.filters = [];
            if (this.store && WebLight.isDefined(this.store.setBaseParam)) {
                this.store.setBaseParam('filters', []);
            }
            return this;
        }

        if (arguments.length > 1) {
            var s = arguments[0];
            for (var i = 0; i < arguments.length - 1; i++) {
                var reg = new RegExp("\\@" + i, "gm");
                var value = arguments[i + 1];
                if (WebLight.isArray(value))
                { value = value.join(','); }
                else if (WebLight.isDate(value))
                    value = WebLight.encode(value);
                s = s.replace(reg, value);
            }
            predicate = s;
        }

        if (predicate.startsWith('&&') && this.lastPredicate)
            predicate = this.lastPredicate + ' ' + predicate;

        var filters = [];
        var reg = /(^|\s+\&\&\s+)([\w-]+)\s*([\[\]\^\!\<\>\=\%]{1,2})\s*\"?([\w\,\s\:\-]*\w)\"?/g;
        while (match = reg.exec(predicate)) {
            var name = match[2];
            var operator = match[3];
            var value = match[4];
            /// temp solution

            var field = this.recordType.getField(name);
            if (field) {
                var filter = { field: name, data: { value: value, type: operator == "[]" ? "list" : field.type.type} };
                switch (operator) {
                    case "==":
                        filter.data.comparison = 'eq';
                        break;
                    case ">":
                        filter.data.comparison = 'gt';
                        break;
                    case "<":
                        filter.data.comparison = 'lt';
                        break;
                    case ">=":
                        filter.data.comparison = 'nl';     // not less than
                        break;
                    case "<=":
                        filter.data.comparison = "ng";        // not great than
                        break;
                    case "!=":
                        filter.data.comparison = "nq";
                        break;
                    case "^!":
                        filter.data.comparison = "ns";
                        break;
                    case "%%":
                        filter.data.comparison = "ct";
                        break;
                    case "^=":
                        filter.data.comparison = "sw";
                        break;
                }
                filters.push(filter);
            }
        }
        this.lastPredicate = predicate;
        this.store.setBaseParam('filters', filters);
        this.params.filters = filters;

        return this;
    },

    property: function (key, value) {

        if (!this._properties)
            this._properties = {};

        if (key === null) {
            // if argument is null, then empty properties
            this._properties = {};
            this.store.setBaseParam('_properties', Ext.encode(this._properties));
            return this;
        }

        if (arguments.length == 1 && WebLight.isString(arguments[0]))
            return this._properties[key];

        var newProperties;
        if (!WebLight.isObject(key)) {
            /// cannot use  key = { key: value };
            newProperties = {};
            newProperties[key] = value;
        }
        else
            newProperties = key;

        WebLight.apply(this._properties, newProperties);
        // remove null items
        for (var item in this._properties) {
            /// must use === instead of ==, if use ==,  value == false will be removed
            if (this._properties[item] === undefined || this._properties[item] === null || this._properties[item] === '')
                delete this._properties[item];
        }

        this.store.setBaseParam('_properties', Ext.encode(this._properties));
        return this;
    },

    load: function () {
        if (this.store && WebLight.isDefined(this.store.load))
            this.store.load();
        return this;
    }
});
/// <reference path="../../references/jquery-1.4.1.js" />
/// <reference path="../Core.js" />

WebLight.data.Store = Ext.extend(Ext.data.Store, {

    trackingChanges: false,

    /// Your changes have not been saved. \r\n\r\n Discard the changes?
    discardMessage: 'Your changes have not been saved. \r\n\r\n Discard the changes?',

    constructor: function (config) {
        config = config || {};
        var me = this;
        var current = this;

        WebLight.data.Store.superclass.constructor.call(this, config);

        this.changes = [];

        this.boundControls = [];

        var computeFields = function (record) {
            if (null == me._computedFields)
                return;

            WebLight.each(me._computedFields, function (item, index) {
                var value = 0;
                switch (item[1]) {
                    case 'func':
                        value = item[2](record);
                        break;
                    case 'sum':
                        var value = 0;
                        WebLight.each(item[2], function (field, fieldIndex) {
                            value += record.get(field) || 0;
                        });
                        break;
                };
                if (record.get(item[0] != value))
                    record.set(item[0], value);
            });
        };

        var calculateRows = function (record) {
            if (null == me._rowSummaryRelations)
                return;

            WebLight.each(me._rowSummaryRelations, function (relation, index) {
                if (!record || relation[2].indexOf(record) != -1) {
                    WebLight.each(relation[1], function (column) {
                        var value = 0;
                        WebLight.each(relation[2], function (r) {
                            var rValue = r.get(column);
                            if (rValue)
                                value += rValue;
                        });

                        if (relation[0].get(column) != value)
                            relation[0].set(column, value);

                    });
                }
            });
        };


        this.on('add', function (store, records, index) {
            Ext.each(records, function (record) {
                if (store.changes.indexOf(record) == -1) {
                    //TODO:
                    //                    record.set('_sysStatus', 1);
                    //                    record.set('_sysName', this.otype);
                    store.changes.push(record);
                    computeFields(record);
                    calculateRows(record);
                }
            }, this);
        });

        this.on('remove', function (store, record, index) {
            var sysStatus = record.get('_sysStatus');
            if (sysStatus == 1)
            //if remove new record, just take it out from changes array
                store.changes.remove(record);
            else if (store.changes.indexOf(record) < 0) {
                // push record in changes
                store.changes.push(record);
            }
            record.set('_sysStatus', 3);
        });

        this.on('update', function (store, record, action) {
            if (action == Ext.data.Record.REJECT || action == Ext.data.Record.COMMIT) {
                if (store.changes.indexOf(record) >= 0)
                    store.changes.remove(record);
                /// remove rejected new record
                if (action == Ext.data.Record.REJECT && record.get('_sysStatus') == 1)
                    this.remove(record);
            }
            else if (action == Ext.data.Record.EDIT) {
                if (store.changes.indexOf(record) == -1) {
                    store.changes.push(record);
                    if (record.get('_sysStatus') != 1)
                        record.set('_sysStatus', 2);
                }
                computeFields(record);
                calculateRows(record);
            }
        });

        var maskControls = function (message) {
            WebLight.each(current.boundControls, function (item, index, array) {
                if (item.getEl) {
                    var el = item.getEl();
                    if (el && el.mask && el.isMasked && !el.isMasked())
                        el.mask(message, 'x-mask-loading');
                }
            }, current);
        };

        var unmaskControls = function () {
            WebLight.each(current.boundControls, function (item, index, array) {
                if (item.getEl) {
                    var el = item.getEl();
                    ///ATTENTION, cannot check el.isMasked(), 
                    /// because if element is hidden or parent is hidden, it always return false even if masked
                    if (el && el.unmask)    // && el.isMasked && el.isMasked())
                        el.unmask();
                }
            }, current);
        };

        this.on('beforeload', function () {

            maskControls(Ext.LoadMask.prototype.msg);
        }, this);
        this.on('load', function () {
            /// ensure selectedIndex less than page count;
            current.selectedIndex(current.selectedIndex());
            /// let method executed after other event listener
            unmaskControls();
        });

        this.on('beforesubmit', function () {
            maskControls('Saving');
        });
        this.on('submit', function () {
            unmaskControls();
        });

        //        me.on('datachanged', function () {
        //            me.changes = [];
        //        });

        me.on('datachanged', function () {
            me.clearComputedFieldSettings();
            me._rowSummaryRelations = null;
            me.changes = [];

            setTimeout(function () {
                me.each(function (record) {
                    computeFields(record);
                });
                calculateRows();
            }, 100);
        });

    },
    bindControl: function (control) {
        if (this.boundControls.indexOf(control) < 0)
            this.boundControls.push(control);
    },
    unbindControl: function (control) {
        if (this.boundControls.indexOf(control) >= 0)
            this.boundControls.remove(control);
    },

    getChangedRecords: function () {
        return this.changes || [];
    },


    rejectChanges: function () {
        WebLight.data.Store.superclass.rejectChanges.call(this);
        this.changes = [];
    },
    /// load data
    load: function (options) {
        if (!this.trackingChanges || this.changes.length == 0)
        { return WebLight.data.Store.superclass.load.call(this, options); }
        else
            Ext.MessageBox.confirm('Confirm', discardMessage, function (btn) {
                if (btn == 'yes')
                    return WebLight.data.Store.superclass.load.call(this, options);
            }, this);
        return false;
    },

    // create new record 
    newRecord: function (defaultData) {
        var me = this;
        if (!WebLight.isDefined(this.recordType)) {
            alert('record Type not defined');
            return null;
        }

        var id = null;
        if (me.idProperty && defaultData && defaultData[me.idProperty])
            id = defaultData[me.idProperty];
        return new this.recordType(Ext.apply(defaultData || {}, { _sysStatus: 1 }), id);
    },

    isNewRecord: function (record) {
        return record.get('_sysStatus') == 1;
    },

    selectedIndex: function () {
        if (!this._selectedIndex)
            this._selectedIndex = 0;

        if (!arguments || arguments.length == 0)
            return this._selectedIndex;

        var index = arguments[0];
        if (index < 0)
            index = 0;

        if (this._selectedIndex != index || this._selectedIndex >= this.getCount()) {
            if (this.getCount() == 0)
                this._selectedIndex = -1;
            else
                this._selectedIndex = index;

            if (this._selectedIndex >= this.getCount())
                this._selectedIndex = 0;
        }
        /// CANNOT put fireEvent in above scope, because if store reload,and index in count range, the event not fired
        /// but it need be fired, because need update formpanel selected record
        this.fireEvent('selectedIndexChanged', this, this._selectedIndex);
        return this;
    },

    _rowSummaryRelations: null,

    addRowRelations: function (type, source, target, columns) {
        var me = this;
        if (type == 'sum') {
            if (me._rowSummaryRelations == null)
                me._rowSummaryRelations = [];

            var sources = source;
            if (!WebLight.isArray(source))
                sources = [source];

            var targetExisted = false;
            WebLight.each(me._rowSummaryRelations, function (item, index) {
                if (item[0] == target) {
                    item[2] = item[2].concat(sources).unique();
                    targetExisted = true;
                    return true;
                }
            });

            if (!targetExisted)
                me._rowSummaryRelations.push([target, columns, sources]);

        }
        return me;
    },



    _computedFields: null,
    clearComputedFieldSettings: function () {
        var me = this;
        me._computedFields = null;
    },
    setComputedField: function (field, computeType, expression) {
        var me = this;
        if (me._computedFields == null)
            me._computedFields = [];

        WebLight.each(me._computedFields, function (item, index) {
            if (item[0] == field) {
                me._computedFields.remove(item);
                return true;
            }
        });

        me._computedFields.push([field, computeType, expression]);
    }
});

/// <reference path="../../references/jquery-1.4.1.js" />

/// <reference path="../Core.js" />

// private
WebLight.data.JsonHttpProxy = Ext.extend(Ext.data.HttpProxy, {
    doRequest: function (action, rs, params, reader, cb, scope, arg) {
        params = params || {};
        delete params.jsonData;
        params.jsonData = Ext.apply({}, params);
        WebLight.data.JsonHttpProxy.superclass.doRequest.call(this, action, rs, params, reader, cb, scope, arg);
    }
});

WebLight.data.JsonReader = Ext.extend(Ext.data.JsonReader, {

    /**
    * This method is only used by a DataProxy which has retrieved data from a remote server.
    * @param {Object} response The XHR object which contains the JSON data in its responseText.
    * @return {Object} data A data block which is used by an Ext.data.Store object as
    * a cache of Ext.data.Records.
    */
    read: function (response) {
        var json = response.responseText;
        var o = Ext.decode(json);
        //        if (!o) {
        //            throw { message: 'JsonReader.read: Json object not found' };
        //        }
        if (o && o.status == 200)
            return this.readRecords(o.result);
        else if (o && o.status == 401) {
            WebLight.PageMgr.fireEvent('accessDenied');
            return this.readRecords({ totalRecords: 0, data: [] });
        }
        else {
            var message = o.result.message;
            if (!message)
                message = 'Unknown error! Failed to load data.';
            alert(message);
            return this.readRecords({ totalRecords: 0, data: [] });
        }
    }

});

WebLight.data.JsonStore = Ext.extend(WebLight.data.Store, {

    /// object type
    otype: '',

    constructor: function (config) {
        config = config || {};
        Ext.apply(config, {
            root: 'data',
            totalProperty: 'totalRecords'
        });

        var current = this;

        if (config.otype) {
            if (!config.readUrl)
                config.readUrl = '/weblight.aspx/load';    //WebLight.getProxyUrl('generic', 'filter');
            if (!config.saveUrl)
                config.saveUrl = '/weblight.aspx/save';        //WebLight.getProxyUrl('generic', 'savechanges');
        }

        if (config.readUrl) {
            var proxy = new WebLight.data.JsonHttpProxy({ url: config.readUrl });
            Ext.applyIf(config, { proxy: proxy, remoteSort: true });
        }

        this.paramNames = { start: 'start', limit: 'limit', sort: 'sort', dir: 'dir' };

        /// fix recordType cannot take affect bug
        Ext.apply(config,
          { reader: new WebLight.data.JsonReader(config, config.recordType) });

        WebLight.data.JsonStore.superclass.constructor.call(this, config);

        this.queryable = new WebLight.data.Queryable({ store: this, recordType: config.recordType });

        this.setBaseParam('_sysName', config.otype);

    },

    exportToExcel: function (options, fields, excelName) {
        options = Ext.apply({}, options);
        if (this.sortInfo && this.remoteSort) {
            var pn = this.paramNames;
            options.params = Ext.apply({}, options.params);
            options.params[pn.sort] = this.sortInfo.field;
            options.params[pn.dir] = this.sortInfo.direction;
        }

        options = Ext.applyIf(options || {}, {
            params: {}
        });

        var formattedFields = [];
        Ext.each(fields, function (item, index) {
            if (WebLight.isString(item))
                formattedFields.push({ field: item });
            else
                formattedFields.push(item);
        });

        Ext.applyIf(options.params, this.baseParams);
        if (this.baseParams._properties)
            Ext.apply(options.params, { _properties: this.baseParams._properties });

        //data can be string of parameters or array/object
        data = jQuery.param({ query: Ext.encode(options.params),
            fields: Ext.encode(formattedFields), fileName: excelName || 'export.csv'
        });

        //split params into form inputs
        var inputs = '';
        Ext.each(data.split('&'), function (item) {
            var pair = item.split('=');
            inputs += '<input type="hidden" name="' + pair[0] + '" value="' + pair[1] + '" />';
        });

        //send request
        jQuery('<form target="_blank" action="' + WebLight.getProxyUrl('__generic', 'exportToExcel') + '" method="' + 'post' + '">' + inputs + '</form>')
		.appendTo('body').submit().remove();
    },



    submitChanges: function (success, failed) {
        /// cannot getChanges before "beforesave" event, 
        /// because used for FormPanel, ensure every fields retrieve.
        this.fireEvent('beforesubmit', this);
        var current = this;
        var getChanges = function (changedRecords) {
            var changes = [];
            /// temp fix
            /// because sometimes add and update executed meantime, like formPanel create mode, changed one field
            var addedRecords = [];

            Ext.each(changedRecords, function (item) {
                if (addedRecords.indexOf(item) == -1) {
                    changes.push(Ext.apply(item.data, { '_sysName': current.otype }));
                    addedRecords.push(item);
                }
            });
            return changes;
        };

        var changes = getChanges(this.changes);
        if (changes.length == 0) {
            if (success)
                success({ affectedCount: 0, data: [] });
            current.fireEvent('submit', this);
        }
        else {
            var store = this;
            WebLight.wcfPost(this.saveUrl,
            Ext.encode({ changes: changes }),
            function (response) {
                if (response) {
                    if (response.status == 401)
                        WebLight.PageMgr.fireEvent('accessDenied');
                    if (response.status == 200) {
                        this.changes = [];
                        if (success)
                            success(response.result);
                    }
                    else {
                        var message = response.result.message;
                        if (!message)
                            message = 'Unknown error! Failed to load data.';
                        if (failed)
                            failed(response.status, message);
                    }
                }
                current.fireEvent('submit', this);
            },
            function (response) {
                if (failed) failed(500, response.responseText);
                current.fireEvent('submit', this);
            });
        }
        return this;
    }
});

/// <reference path="../Core.js" />


/**
* @class WebLight.data.PagingMemoryProxy
* @extends Ext.data.MemoryProxy
* <p>Paging Memory Proxy, allows to use paging grid with in memory dataset</p>
*/
WebLight.data.PagingMemoryProxy = Ext.extend(Ext.data.MemoryProxy, {
    constructor: function (data) {
        WebLight.data.PagingMemoryProxy.superclass.constructor.call(this);
        this.data = data;
    },
    doRequest: function (action, rs, params, reader, callback, scope, options) {
        params = params ||
        {};
        var result;
        try {
            result = reader.readRecords(this.data);
        }
        catch (e) {
            this.fireEvent('loadexception', this, options, null, e);
            callback.call(scope, null, options, false);
            return;
        }

        // filtering
        if (params.filter !== undefined) {
            result.records = result.records.filter(function (el) {
                if (typeof (el) == 'object') {
                    var att = params.filterCol || 0;
                    return String(el.data[att]).match(params.filter) ? true : false;
                }
                else {
                    return String(el).match(params.filter) ? true : false;
                }
            });
            result.totalRecords = result.records.length;
        }

        // sorting
        if (params.sort !== undefined) {
            // use integer as params.sort to specify column, since arrays are not named
            // params.sort=0; would also match a array without columns
            var dir = String(params.dir).toUpperCase() == 'DESC' ? -1 : 1;
            var fn = function (v1, v2) {
                return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
            };
            result.records.sort(function (a, b) {
                var v = 0;
                if (typeof (a) == 'object') {
                    v = fn(a.data[params.sort], b.data[params.sort]) * dir;
                }
                else {
                    v = fn(a, b) * dir;
                }
                if (v == 0) {
                    v = (a.index < b.index ? -1 : 1);
                }
                return v;
            });
        }
        // paging (use undefined cause start can also be 0 (thus false))
        if (params.start !== undefined && params.limit !== undefined) {
            result.records = result.records.slice(params.start, params.start + params.limit);
        }
        callback.call(scope, result, options, true);
    }
});
/// <reference path="../references/jquery-1.4.1.js" />
/// <reference path="Core.js" />
/// <reference path="util/Observable.js" />
/// <reference path="util/MixedCollection.js" />
/// <reference path="plugins/jquery.history.js" />
/// <reference path="util/JSON.js" />

/*
Examples:

WebLight.Router.mapRoute('campaigns/([a-z]+)\:((?:[a-z]+=[a-z]+/?)+)', { xtype: 'page', 'campaignId': '{0}', 'status': '{1}' });
WebLight.Router.mapRoute('events/([a-z]+)/test=([a-z]+)', function(url,match) {
    return WebLight.encode(String.format(" { xtype: 'page', 'campaignId': '{0}', 'status': '{1}' }",match[1],match[2]));
});
Ext.onReady(function() {
    WebLight.Router.init('campaigns/details:campaignId=adfasda&');
});



*/

WebLight.Router = (function () {

    /// registered url patterns
    var _routeUrlPatterns = [];
    /// registered url associated configs
    var _routeConfigs = [];

    var _defaultConfig = null;

    var _currentUrl = '/';

    /// get page config by url, and apply matched values to original config
    var execute = function (url) {
        var page = null;

        /// loop patterns check if has pattern matched the url, if matched, then executed associated function
        $.each(_routeUrlPatterns, function (index, pattern) {
            /// cannot use regex.test, then use reg.match, if do this way, reg.match cannot get first item
            var regex = new RegExp(pattern, 'gi');
            if (match = regex.exec(url)) {
                var config = null;
                if (WebLight.isFunction(_routeConfigs[index]))
                    config = _routeConfigs[index](url, match);
                else {
                    var config = WebLight.apply({}, _routeConfigs[index]);
                    var configStr = WebLight.encode(config);
                    for (var i = 0; i < match.length - 1; i++) {
                        var reg = new RegExp("\\{" + i + "\\}", "gm");
                        configStr = configStr.replace(reg, match[i + 1]);
                    }
                    WebLight.apply(config, WebLight.decode(configStr));
                }
                if (_defaultConfig && _defaultConfig.renderTo && (!config.renderTo || config.renderTo == ''))
                    config.renderTo = _defaultConfig.renderTo;
                if (!config.id)
                    config.id = config.xtype;

                page = WebLight.PageMgr.load(url, config);
                /// return false to break current loop
                return false;
            }
        });

        if (null != page)
            _currentUrl = url;

        return page;
    };

    return {
        /// add history feature
        /// if use extjs, cannot use $(document).ready, because sometimes Extjs not initialized
        /// if just use jquery , put it in $(document).ready
        /// {renderTo:'content',dashboard:'homepage'}
        init: function (config) {
            "config:nomunge";
            _defaultConfig = config;

            // Initialize history plugin.
            // The callback is called at once by present location.hash.
            $.historyInit(function (hash) {
                if (hash)
                    execute.defer(200, this, [hash]);           // execute(hash);
            });

            if (location.hash.replace(/\?.*$/, '') == '') {
                if (config.defaultUrl)
                    WebLight.Router.route(config.defaultUrl);
                ///obsolete
                else if (config.dashboard)
                    WebLight.Router.route(config.dashboard);
            }
        },
        mapRoute: function (pattern, func) {
            "pattern:nomunge, func:nomunge";
            /*
            It is possible to prevent a local variable, nested function or function
            argument from being obfuscated by using "hints". A hint is a string that
            is located at the very beginning of a function body like so:
            */
            /// cannot create regex and cache it, 
            /// it has a bug if exec 2 strings one by one
            _routeUrlPatterns.push(pattern);
            _routeConfigs.push(func);
        },
        route: function (url, callback) {
            "url:nomunge, callback:nomunge";
            url = url.trim();

            if (url.endsWith('/'))
                url = url.substr(0, url.length - 1);

            if (url.startsWith('/'))
                url = url.substr(1);
            else {
                /// Url combine if not starts with /
                var urlPrefix = _currentUrl.substr(0, _currentUrl.lastIndexOf('/'));
                while (url.substr(0, 3) == '../') {
                    urlPrefix = urlPrefix.substr(0, urlPrefix.lastIndexOf('/'));
                    url = url.substr(3);
                }
                url = urlPrefix + '/' + url;
            }

            var page = execute(url);

            if (null != page) {
                $.historyLoad(url);

                if (callback)
                    callback(page);
            }
            return page;
        }
    };

})();

/// <reference path="../references/jquery-1.4.1.js" />
/// <reference path="Core.js" />
/// <reference path="util/Observable.js" />

WebLight.Control = Ext.extend(WebLight.util.Observable, {

    childControlsCreated: false,

    renderTo: '',

    html: null,

    $this: null,

    constructor: function (config) {

        var me = this;
        WebLight.Control.superclass.constructor.call(this, config);
        Ext.apply(this, config);

        if (!me.id)
            me.id = 'wl_ctrl_' + WebLight.Control.globalSequenceId++;

        me._controls = [];

        /// registered controls which container not inherit from WebLight.Control, like TabPanel
        /// for now it used for unload method
        me._regControls = [];

        /// create container
        me.$this = $('<div id="' + me.id + '" style="display:none"></div>').appendTo(document.body);

        //        if (this.html)
        //            $(this.formatTemplate(this.html)).appendTo(this.$this);
        me.loadTemplate(me.html, me.$this);

        me.childControlsCreated = false;

        me.rendered = false;
        me.addEvents('render', 'hibernate', 'resume', 'destroy');
    },

    loadTemplate: function (html, container) {
        var me = this;
        if (html) {
            container = container || me.$this;
            if (WebLight.isString(container) && container.substr(0, 1) != '#')
                container = "#" + container;
            $(html.replace(/id=\"/gi, 'id="' + me.id + '_').replace(/href=\"\#(?=[a-zA-Z])/gi,
                'href="#' + me.id + '_')).appendTo($(container));
        }
    },

    ensureChildControls: function () {
        var me = this;
        if (me.childControlsCreated)
            return me;

        me.createChildControls();
    },

    $child: function (id) {
        return jQuery('#' + this.getClientId(id));
    },

    $find: function (selector) {
        var me = this;
        return jQuery(selector, me.$this);
    },

    getClientId: function (id) {
        return String.format('{0}_{1}', this.id, id);
    },
    createChildControls: function () {
        var me = this;
        me.childControlsCreated = true;
    },

    addChildControl: function (control, containerId) {
        var me = this;
        if (containerId)
            control.containerId = containerId;
        me._controls.push(control);
        if (me.rendered) {
            if (control.containerId)
                control.render(me.getClientId(control.containerId));
            else
                control.render(me.$this);
        }
    },

    registerControl: function (control) {
        var me = this;

        if (me._controls.indexOf(control) >= 0)
            return me;

        if (me._regControls.indexOf(control) == -1)
            me._regControls.push(control);
        return me;
    },

    dataBind: function () {

    },


    renderChildControls: function () {
        var me = this;
        WebLight.each(me._controls, function (item, index, array) {
            if (item.containerId)
                item.render(me.getClientId(item.containerId));
            else
                item.render(me.$this);
        }, me);
    },

    doLayout: function () {
        var me = this;
        WebLight.each(this._controls, function (item, index, array) {
            if (item.doLayout)
                item.doLayout();
        }, me);
    },

    render: function (renderTo) {
        var me = this;
        var renderTargetChanged = WebLight.isDefined(renderTo) || me.renderTo != renderTo;
        if (WebLight.isDefined(renderTo) && me.renderTo != renderTo) {
            this.renderTo = renderTo;
        }

        if (renderTargetChanged) {
            /// move to specified html container.
            if (WebLight.isString(me.renderTo))
                me.$this.appendTo('#' + me.renderTo);
            else
                me.$this.appendTo(me.renderTo);
        }

        if (me.rendered)
            return;

        me.ensureChildControls();

        ///ATTENTION: must show container first, then render child controls
        /// if reversed, grid pagingbar cannot render correctly.

        $(me.$this).show();
        me.renderChildControls();
        me.dataBind();
        me.rendered = true;
        me.fireEvent('render', me);
    },



    resume: function () {
        var me = this;
        me.fireEvent('resume', me);

        WebLight.each(me._controls, function (item, index, array) {
            if (item.resume)
                item.resume();
        }, me);

        $(me.$this).show();
        return me;
    },

    _unload: function () {
        var me = this;
        var unloaded = true;
        WebLight.each(me._controls, function (item, index, array) {
            if (WebLight.isFunction(item._unload) && !item._unload()) {
                unloaded = false;
                return false;
            }
        }, me);
        if (!unloaded)
            return false;

        WebLight.each(me._regControls, function (item, index, array) {

            if (WebLight.isFunction(item._unload) && !item._unload()) {
                unloaded = false;
                return false;
            }
        }, me);

        if (!unloaded)
            return false;

        return me.fireEvent('beforeunload');
    },

    hibernate: function () {
        var me = this;
        $(me.$this).hide();
        me.fireEvent('hibernate', me);

        WebLight.each(me._controls, function (item, index, array) {
            if (item.hibernate)
                item.hibernate();
        }, me);

        return me;
    },

    destroy: function () {
        var me = this;
        me.fireEvent('destroy', this);

        /// temp comment out, looks like has bug
        //        WebLight.each(this._controls, function (item, index, array) {
        //            if (item.destroy)
        //                item.destroy();
        //        }, this);

        if (me.$this)
            me.$this.remove();

        me.$this = null;
    }

});

WebLight.Control.globalSequenceId = 100;

/// <reference path="Control.js" />

WebLight.override(WebLight.Control, {

    getEl: function () { return Ext.get(this.id); },

    grouping: function (items, callbacks) {
        var me = this;

        if (!callbacks)
            callbacks = {};

        /// safe mask control
        var maskIt = function (ctrl, message) {
            if (ctrl && ctrl.getEl) {
                var el = ctrl.getEl();
                if (el.isMasked && !el.isMasked())
                    el.mask(message);
            }
        };

        /// unmask 
        var unmask = function (ctrl) {
            if (ctrl && ctrl.getEl) {
                var el = ctrl.getEl();
                if (el)
                    el.unmask();
            }
        };

        var validCtrls = [];
        var validStores = [];

        Ext.each(items, function (item, index) {
            if (item.getEl)
                validCtrls.push(item);
            else if (item.getTotalCount)
                validStores.push(item);
        });

        var loadingStores = [];

        var beforeHandler = function (store, message, callback) {
            if (loadingStores.length == 0) {
                if (!callback || callback() !== false) {
                    Ext.each(validCtrls, function (ctrl, i) {
                        maskIt(ctrl, message);
                    });
                }
            }
            loadingStores.push(store);
        };

        var afterHandler = function (store, callback) {
            loadingStores.remove(store);
            if (loadingStores.length == 0) {
                (function () {
                    if (loadingStores.length == 0) {
                        if (callback)
                            callback();
                        Ext.each(validCtrls, function (ctrl, i) {
                            unmask(ctrl);
                        });
                    }
                }).defer(200);
            }
        };


        Ext.each(validStores, function (store, index) {
            store.on('beforeload', function () {
                beforeHandler(store, 'Loading...', callbacks.beforeload);
            });

            store.on('beforesubmit', function () {
                beforeHandler(store, 'Saving...', callbacks.beforesubmit);
            });

            store.on('load', function () {
                afterHandler(store, callbacks.load);
            });

            store.on('submit', function () {
                afterHandler(store, callbacks.submit);
            });
        });
    }
});


WebLight.Control.ExtjsWrapper = function (ctrl, config) {
    config = WebLight.apply({
        xtype: 'box',
        autoEl: { tag: 'div' },
        listeners: {
            render: function () {
                var panelBody = $('#' + this.id + ' .x-panel-body');
                if (panelBody && panelBody.length) {
                    ctrl.render(panelBody);
                }
                else
                    ctrl.render(this.id);
            }
        }
    }, config);

    return new Ext.create(config);

};
/// <reference path="../references/jquery-1.4.1.js" />
/// <reference path="Core.js" />
/// <reference path="util/Observable.js" />

WebLight.Page = Ext.extend(WebLight.Control, {

    cacheable: false,

    title: 'Untitled',

    constructor: function (config) {

        WebLight.Page.superclass.constructor.call(this, config);
        Ext.apply(this, config);

        this.isActive = false;
    },

    render: function (renderTo) {

        WebLight.Page.superclass.render.call(this, renderTo);

        if (document.title != this.title)
            document.title = this.title;
        $(window).bind('resize', this.doLayout);
        this.isActive = true;
    },

    hibernate: function () {

        WebLight.Page.superclass.hibernate.call(this);
        $(window).unbind('resize', this.doLayout);
        this.isActive = false;
        return this;
    },

    resume: function () {

        WebLight.Page.superclass.resume.call(this);
        $(window).bind('resize', this.doLayout);
        this.setTitle(this.title);
        this.isActive = true;
        return this;
    },

    setTitle: function (title) {
        if (title)
            this.title = title;
        document.title = this.title;
        return this;
    }

});
/// <reference path="../references/jquery-1.4.1.js" />
/// <reference path="Core.js" />
/// <reference path="util/Observable.js" />
/// <reference path="util/MixedCollection.js" />

WebLight.PageMgr = function () {

    var pages = new WebLight.util.MixedCollection();
    var types = {};

    var pageMgr = WebLight.extend(WebLight.util.Observable,
     {
         constructor: function (config) {
             pageMgr.superclass.constructor.call(this, config);
             this.addEvents('load', 'resume', 'hibernate');
         },
         /**
         * <p>Registers a new Component constructor, keyed by a new
         * {@link Ext.Component#xtype}.</p>
         * <p>Use this method (or its alias {@link Ext#reg Ext.reg}) to register new
         * subclasses of {@link Ext.Component} so that lazy instantiation may be used when specifying
         * child Components.
         * see {@link Ext.Container#items}</p>
         * @param {String} xtype The mnemonic string by which the Component class may be looked up.
         * @param {Constructor} cls The new Component class.
         */
         registerType: function (xtype, cls) {
             if (undefined != types[xtype] && types[xtype] != cls) {
                 alert("page xtype mapping conflict. xtype is " + xtype);
                 return;
             }
             types[xtype] = cls;
             cls.xtype = xtype;
         },

         /**
         * Checks if a Component type is registered.
         * @param {Ext.Component} xtype The mnemonic string by which the Component class may be looked up
         * @return {Boolean} Whether the type is registered.
         */
         isRegistered: function (xtype) {
             return types[xtype] !== undefined;
         },

         /**
         * Creates a new Component from the specified config object using the
         * config object's {@link Ext.component#xtype xtype} to determine the class to instantiate.
         * @param {Object} config A configuration object for the Component you wish to create.
         * @param {Constructor} defaultType The constructor to provide the default Component type if
         * the config object does not contain a <code>xtype</code>. (Optional if the config contains a <code>xtype</code>).
         * @return {Ext.Component} The newly instantiated Component.
         */
         create: function (config, defaultType) {
             return config.render ? config : new types[config.xtype || defaultType](config);
         },

         getCurrentPage: function () {
             return pages.last();
         },
         load: function (url, config, defaultType) {
             var page = null;
             var lastPage = pages.last();


             var unloadLastPage = function () {
                 if (!lastPage)
                     return true;

                 // Fires before a page is unloaded. 
                 // If the beforeunload handler returns false the unload action will be canceled.
                 if (!lastPage._unload())
                     return false;

                 if (lastPage.cacheable && lastPage.id) {
                     lastPage.hibernate();
                     WebLight.PageMgr.fireEvent('hibernate', url, lastPage);
                 }
                 else {
                     pages.remove(lastPage);
                     lastPage.destroy();
                     delete lastPage;
                     lastPage = null;
                 }
                 return true;
             };

             if (config.id) {
                 /// check if page cached
                 page = pages.get(config.id);
                 if (page) {
                     // if page in hibernate status
                     if (!page.isActive) {
                         // hibernate or destroy last page
                         if (!unloadLastPage())
                             return null;

                         /// take page to last position
                         pages.remove(page);
                         pages.add(page);

                         // resume page
                         page.resume();
                         this.fireEvent('resume', url, page);
                     }
                     return page;
                 }
             }

             if (!unloadLastPage())
                 return null;

             page = WebLight.PageMgr.create(config, defaultType);
             pages.add(page);

             page.render();
             this.fireEvent('load', url, page);
             return page;
         }
     });
    return new pageMgr();
} ();
/// <reference path="../../references/jquery-1.4.1.js" />
/// <reference path="../../references/ext-jquery-adapter.js" />
/// <reference path="../../references/ext-all.js" />

/// <reference path="../Core.js" />

WebLight.form.FormPanel = Ext.extend(Ext.form.FormPanel, {

    initComponent: function () {

        this.addEvents(
            'bound', 'storechanged'
        );
        var current = this;
        WebLight.form.FormPanel.superclass.initComponent.call(this);

        this._fields = new Ext.util.MixedCollection();

        /// update current bound
        /// if record is null ,act as unbind feature
        var updateBound = function (record) {

            current.boundRecord = record;
            current.form.loadRecord(record ? record : { data: {} });

            if (record)
                current.fireEvent('bound', record);
        };

        /// bind field value to record
        var bindFieldValue = function (field) {
            if (current.boundRecord && field.name) {
                var value = field.getValue();
                if (Ext.isArray(value)) {
                    /// checkboxgroup
                    var newValue = [];
                    Ext.each(value, function (item) {
                        newValue.push(item.name);
                    });
                    value = newValue;
                }
                else if (Ext.isObject(value)) {
                    // maybe radiogroup
                    if (value.inputValue)
                        value = value.inputValue;
                }
                current.boundRecord.set(field.name, value);
            }
        };

        var attachChangeEvent = function (f) {
            f.on({
                scope: current
                    , change: function (field) {
                        if (!this.boundRecord && this.store) {
                            //  var r = Ext.data.Record.create(this.store.fields);
                            //   var record = new r();
                            var record = this.store.newRecord();
                            this.store.add(record);
                            updateBound(record);
                        }
                        bindFieldValue(field);
                    }
            });
        };

        this.getForm().items.each(function (f) {
            // register field if name defined. 
            if (f.name) {
                this._fields.add(f.name, f);
                attachChangeEvent(f);
            }

            if (f.xtype == 'compositefield' || f.xtype == 'checkboxgroup') {
                /// must do it in render method
                /// because compositefield not initialize, it still config, and after it rendered, the items will convert to MixedCollection, not array
                /// cannot use Ext.each method.
                f.on('render', function () {
                    f.items.each(function (innerField) {
                        if (innerField.name/* && innerField.xtype != 'radio'*/) {
                            current._fields.add(innerField.name, innerField);
                            attachChangeEvent(innerField);
                        }
                    });
                });
            }
            /// apply no-field-label class to displayfield if fieldLabel not definied.
            else if (f.xtype == 'displayfield') {
                if (!f.fieldLabel || f.fieldLabel == '')
                    f.on('render', function () { f.el.up('.x-form-item', 10, true)/*.child('.x-form-item-label')*/.addClass('no-field-label'); }, f);
            }

        }, this);

        var initStore = function (store) {
            /// set page size to 1
            //store.setBaseParam(store.paramNames.limit, 1);

            if (store.bindControl)
                store.bindControl(current);

            store.on('beforesubmit', function () {

                // sometimes bind store to 2 formpanels, like View mode and Edit mode
                /// we need ingore view mode because it not changed.
                /// if not ,it will be some issue, because current.bindRecord is null
                if (!current.boundRecord)
                    return;

                /// because changed reject feature, if call form.reset method
                /// record will be removed from store
                /// if submit ,need ensure new record in store
                if (current.store.indexOf(current.boundRecord) == -1)
                    current.store.add(current.boundRecord);

                // sometimes combobox or checkbox cannot save value to record correctly.
                // below code ensure all fields applied to record.
                current._fields.each(function (item, index, length) {
                    if (item.xtype != 'displayfield')
                        bindFieldValue(item);
                }, current);
            }, this);

            if (store.getCount() > 0) {
                if (store.selectedIndex)
                    updateBound(store.getAt(store.selectedIndex()));
                else
                    updateBound(store.getAt(0));
            }

            /// because store did it ,no need do it twice
            //            store.on('load', function () {
            //                if (store.selectedIndex)
            //                    updateBound(store.getAt(store.selectedIndex()));
            //                else
            //                    updateBound(store.getAt(0));
            //            });

            store.on('selectedIndexChanged', function () {
                if (store.selectedIndex)
                    updateBound(store.getAt(store.selectedIndex()));
                else
                    updateBound(store.getAt(0));
            });
        };

        if (this.store) {
            initStore(this.store);
        }

        this.on('storechanged', function (newStore, oldStore) {
            initStore(newStore);
            if (oldStore && oldStore.unbindControl)
                oldStore.unbindControl(current);
        });

    },

    getBoundRecord: function () {
        return this.boundRecord;
    },

    getFieldByName: function (name) {
        if (this._fields && this._fields.containsKey(name))
            return this._fields.get(name);
        return null;
    },

    getFieldValue: function (name) {
        if (this._fields && this._fields.containsKey(name))
            return this._fields.get(name).getValue();
        return null;
    },

    getFieldValues: function () {
        var data = {};
        this.getForm().items.each(function (f) {
            if (f.xtype == 'checkboxgroup') {
                if (f.name)
                /// value config on checkboxgroup level
                    data[f.name] = f.getValue();
                else {
                    f.items.each(function (innerField) {
                        if (innerField.name) {
                            data[innerField.name] = innerField.getValue();
                        }
                    });
                }
            }
            else if (f.xtype == 'compositefield') {
                f.items.each(function (innerField) {
                    if (innerField.name && innerField.xtype != 'displayfield' /*&& innerField.xtype != 'radio'*/) {
                        data[innerField.name] = innerField.getValue();
                    }
                });
            }
            else if (f.name && f.xtype != 'displayfield') {
                data[f.name] = f.getValue();
            }
        }, this);
        return data;
    },

    bindStore: function (store) {
        if (store != this.store) {
            var oldStore = this.store;
            this.store = store;
            this.fireEvent('storechanged', store, oldStore);
        }
    },

    /// reject changed fields, reset to default value
    reset: function () {
        if (this.boundRecord) {
            this.boundRecord.reject();
            this.form.loadRecord(this.boundRecord);
        }
        else {
            this.getForm().items.each(function (f) {
                f.reset();
            }, this);
            this.getForm().reset();
        }
        //        this.getForm().items.each(function(f) {
        //            f.reset();
        //        }, this);
        //        this.getForm().reset();
    }
});
/// <reference path="../../references/jquery-1.4.1.js" />
/// <reference path="../../references/ext-jquery-adapter.js" />
/// <reference path="../../references/ext-all.js" />

/// <reference path="../Core.js" />

WebLight.form.ComboBox = Ext.extend(Ext.form.ComboBox, {

    initComponent: function () {
        var me = this;
        if (!this.displayField || this.displayField == '')
            this.displayField = 'value';

        if (!this.valueField || this.valueField == '')
            this.valueField = 'key';

        if (this.store) {
            this.store.on('load', function (store, records) {
                this.clearValue();
                Ext.each(records, function (item, index) {

                    if (item.get(me.valueField) == me.innerValue)
                        me.setValue(me.innerValue);
                });
            }, this);
        }

        WebLight.form.ComboBox.superclass.initComponent.call(this);

    },

    setValue: function (v) {
        var oldValue = this.value;
        this.innerValue = v;

        WebLight.form.ComboBox.superclass.setValue.call(this, v);

        if (oldValue != v)
            this.fireEvent('valuechange', v, this.getRawValue());

        return this;
    },

    filterFieldName: null,

    linkTo: function (parent, filterName, filterType) {
        parent.on('valuechange', function (value) {
            this.clearValue();
            if (!value || value == '') {
                this.store.removeAll();
                return;
            }
            var filter = { field: filterName || parent.valueField, data: { type: filterType || 'string', value: value} };
            if (!this.store.baseParams)
                this.store.baseParams = {};
            if (!this.store.baseParams.filters)
                this.store.baseParams.filters = [];
            var matched = false;
            Ext.each(this.store.baseParams.filters, function (f) {
                if (f.field.toLowerCase() == filter.field.toLowerCase())
                { f.data.value = value; matched = true; }
            });
            if (!matched)
                this.store.baseParams.filters.push(filter);
            this.store.load();
        }, this);
    }
});

/// <reference path="../Core.js" />

WebLight.form.FormView = WebLight.extend(WebLight.Control, {

    constructor: function (config) {

        var me = this;

        me.addEvents('bound', 'storechanged');

        me._fields = new Ext.util.MixedCollection();

        var setValues = function (values) {

            if (Ext.isArray(values)) { // array of objects
                for (var i = 0, len = values.length; i < len; i++) {
                    var v = values[i];
                    var f = me.findField(v.name);
                    if (f) {
                        f.setValue(v.value);
                    }
                }
            } else { // object hash
                //                var field, name;
                //                for (name in values) {
                //                    if (!Ext.isFunction(values[name]) && (field = me.findField(name))) {
                //                        field.setValue(values[name]);
                //                    }
                //                }

                me._fields.each(function (item, index, length) {
                    var value = values[item.name];
                    item.setRawValue(value || '');
                }, me);

            }
        };

        var storeUpdateMonitor = function (store, record, operation) {
            if (record == me.boundRecord && operation == Ext.data.Record.EDIT)
                setValues(record ? record.data : {});
        };

        var refreshFields = function () {

            if (me.boundRecord)
                setValues(me.boundRecord.data);
        };

        /// update current bound
        /// if record is null ,act as unbind feature
        var updateBound = function (record) {

            me.boundRecord = record;
            setValues(record ? record.data : {});

            if (record)
                me.fireEvent('bound', record);
        };

        var setValue = function (name, value) {
            if (me.boundRecord && name) {
                // if (me.boundRecord.get(name) != value) {
                if (Ext.isArray(value)) {
                    /// checkboxgroup
                    var newValue = [];
                    Ext.each(value, function (item) {
                        newValue.push(name);
                    });
                    value = newValue;
                }
                else if (Ext.isObject(value)) {
                    // maybe radiogroup
                    if (value.inputValue)
                        value = value.inputValue;
                }
                me.boundRecord.set(name, value);
                //  }
            }
        };

        /// bind field value to record
        var bindFieldValue = function (field) {
            setValue(field.name, field.getValue());
        };

        var attachChangeEvent = function (f) {
            f.on({
                scope: me
                    , change: function (field) {
                        if (!me.boundRecord && me.store) {
                            //  var r = Ext.data.Record.create(this.store.fields);
                            //   var record = new r();

                            // cache value
                            // because updated when update bound
                            var value = field.getValue();

                            var record = me.store.newRecord();
                            me.store.add(record);
                            updateBound(record);
                            setValue(field.name, value);
                        }
                        else
                            bindFieldValue(field);
                    }
            });
        };

        var initField = function (f) {
            // register field if name defined. 
            if (f.name) {
                me._fields.add(f.name, f);
                attachChangeEvent(f);
            }

            if (f.xtype == 'compositefield' || f.xtype == 'checkboxgroup') {
                /// must do it in render method
                /// because compositefield not initialize, it still config, and after it rendered, the items will convert to MixedCollection, not array
                /// cannot use Ext.each method.
                f.on('render', function () {
                    f.items.each(function (innerField) {
                        if (innerField.name/* && innerField.xtype != 'radio'*/) {
                            me._fields.add(innerField.name, innerField);
                            attachChangeEvent(innerField);
                        }
                    });
                });
            }
        };
        me.initField = initField;

        var initStore = function (store) {

            if (store.bindControl)
                store.bindControl(me);

            store.on('update', storeUpdateMonitor);

            store.on('beforesubmit', function () {

                // sometimes bind store to 2 formpanels, like View mode and Edit mode
                /// we need ingore view mode because it not changed.
                /// if not ,it will be some issue, because current.bindRecord is null
                if (!me.boundRecord)
                    return;

                /// because changed reject feature, if call form.reset method
                /// record will be removed from store
                /// if submit ,need ensure new record in store
                if (me.store.indexOf(me.boundRecord) == -1)
                    me.store.add(me.boundRecord);

                // sometimes combobox or checkbox cannot save value to record correctly.
                // below code ensure all fields applied to record.
                me._fields.each(function (item, index, length) {
                    if (item.xtype != 'displayfield')
                        bindFieldValue(item);
                }, me);
            }, this);

            if (store.getCount() > 0) {
                if (store.selectedIndex)
                    updateBound(store.getAt(store.selectedIndex()));
                else
                    updateBound(store.getAt(0));
            }

            store.on('selectedIndexChanged', function () {
                if (store.selectedIndex) {
                    if (store.selectedIndex() == -1)
                        updateBound(null);
                    else
                        updateBound(store.getAt(store.selectedIndex()));
                }
                else
                    updateBound(store.getAt(0));
            });
        };

        WebLight.form.FormView.superclass.constructor.call(this, config);

        if (me.items) {
            WebLight.each(me.items, function (item, index) {
                ctrl = item;
                if (!ctrl.events)
                    ctrl = me.createComponent(item);
                me.addChildControl(ctrl, ctrl.name);
            });
            //            delete me.items;

        }

        if (config.store) {
            initStore(config.store);
        }

        me.on('storechanged', function (newStore, oldStore) {
            initStore(newStore);
            if (oldStore && oldStore.unbindControl) {
                oldStore.unbindControl(me);
                oldStore.un('update', storeUpdateMonitor);
            }
        });

        me.on('render', function () { refreshFields(); });
    },

    loadTemplate: function (html, container) {
        var me = this;
        if (me._container != container) {
            WebLight.form.FormView.superclass.loadTemplate.call(me, html, container);
            return;
        }

        var regex = new RegExp('\{.*?\}', 'gi');
        var match;

        var formattedHtml = html;
        var cmps = [];
        this.cmpIndex = 0;
        while (match = regex.exec(formattedHtml)) {
            var config = Ext.decode(match[0]);
            if (!config.name)
                config.name = String.format('{0}', this.cmpIndex++);

            var replaceHtml = String.format('<span id="{0}"></span>', config.name);
            formattedHtml = formattedHtml.replace(match[0], replaceHtml);
            cmps.push(config);
        }

        WebLight.form.FormView.superclass.loadTemplate.call(this, formattedHtml, container);

        Ext.each(cmps, function (item, index) {
            me.addChildControl(Ext.create(item), item.name);
        });
    },

    // private
    applyDefaults: function (c) {
        var d = this.defaults;
        if (d) {
            if (Ext.isFunction(d)) {
                d = d.call(this, c);
            }
            if (Ext.isString(c)) {
                c = Ext.ComponentMgr.get(c);
                Ext.apply(c, d);
            } else if (!c.events) {
                Ext.applyIf(c, d);
            } else {
                Ext.apply(c, d);
            }
        }
        return c;
    },

    // private
    createComponent: function (config, defaultType) {
        if (config.render) {
            return config;
        }
        var c = Ext.create(config, defaultType || this.defaultType);
        return c;
    },

    addChildControl: function (control, containerId) {
        var me = this;
        WebLight.form.FormView.superclass.addChildControl.call(this, control, containerId);

        if (me.isField(control)) {
            me.initField(control);
        }
        return me;
    },

    addField: function (field) {
        if (this.isField(field))
            this.addChildControl(field, field.name);
    },

    findField: function (name) {
        if (this._fields && this._fields.containsKey(name))
            return this._fields.get(name);
        return null;
    },

    bindStore: function (store) {
        if (store != this.store) {
            var oldStore = this.store;
            this.store = store;
            this.fireEvent('storechanged', store, oldStore);
        }
        return this;
    },

    getFieldByName: function (name) {
        if (this._fields && this._fields.containsKey(name))
            return this._fields.get(name);
        return null;
    },

    // Determine if a Component is usable as a form Field.
    isField: function (c) {
        return !!c.name && !!c.setValue && !!c.getValue && !!c.markInvalid && !!c.clearInvalid;
    }

});
/// <reference path="../Core.js" />
/**
* Creates new Range Field
* @constructor
* @param {Object} config A config object
*/

WebLight.form.RangeField = WebLight.extend(Ext.form.Field, {

    defaultAutoCreate: { tag: 'input', type: 'hidden' },
    separator: '|',

    getStartConfig: function () { },
    getEndConfig: function () { },


    initComponent: function () {
        // call parent initComponent
        WebLight.form.RangeField.superclass.initComponent.call(this);

        var startConfig = WebLight.apply({
            id: this.id + '-start',
            selectOnFocus: this.selectOnFocus,
            validator: this.dateValidator,
            listeners: {
                blur: { scope: this, fn: this.onBlur },
                focus: { scope: this, fn: this.onFocus }
            }
        }, this.getStartConfig(), this.startConfig
        );

        this.sf = Ext.create(startConfig);
        this.sf.ownerCt = this;

        var endConfig = WebLight.apply({
            id: this.id + '-end',
            selectOnFocus: this.selectOnFocus,
            validator: this.dateValidator,
            listeners: {
                blur: { scope: this, fn: this.onBlur },
                focus: { scope: this, fn: this.onFocus }
            }
        }, this.getEndConfig(), this.endConfig
        );

        this.ef = Ext.create(endConfig);
        this.ef.ownerCt = this;

        // relay events
        this.relayEvents(this.sf, ['focus', 'specialkey', 'invalid', 'valid']);
        this.relayEvents(this.ef, ['focus', 'specialkey', 'invalid', 'valid']);

    } // eo function initComponent
    // }}}
    // {{{
    /**
    * @private
    * Renders underlying DateField and TimeField and provides a workaround for side error icon bug
    */
    , onRender: function (ct, position) {
        // don't run more than once
        if (this.isRendered) {
            return;
        }

        // render underlying hidden field
        WebLight.form.RangeField.superclass.onRender.call(this, ct, position);


        t = Ext.DomHelper.append(ct, { tag: 'table', style: 'border-collapse:collapse', children: [
                { tag: 'tr', children: [
                    { tag: 'td', style: 'padding-right:4px', cls: 'ux-rangefield-start' },
                    { tag: 'td', cls: 'ux-rangefield-end' }
                ]
                }
            ]
        }, true);

        this.tableEl = t;
        this.wrap = t.wrap({ cls: 'x-form-field-wrap' });
        //        this.wrap = t.wrap();
        this.wrap.on("mousedown", this.onMouseDown, this, { delay: 10 });
        // render StartField & EndField
        this.sf.render(t.child('td.ux-rangefield-start'));
        this.ef.render(t.child('td.ux-rangefield-end'));

        this.on('specialkey', this.onSpecialKey, this);
        this.sf.el.swallowEvent(['keydown', 'keypress']);
        this.ef.el.swallowEvent(['keydown', 'keypress']);

        // create icon for side invalid errorIcon
        if ('side' === this.msgTarget) {
            var elp = this.el.findParent('.x-form-element', 10, true);
            this.errorIcon = elp.createChild({ cls: 'x-form-invalid-icon' });

            var o = {
                errorIcon: this.errorIcon
                , msgTarget: 'side'
                , alignErrorIcon: this.alignErrorIcon.createDelegate(this)
            };
            Ext.apply(this.sf, o);
            Ext.apply(this.ef, o);
            //            this.df.errorIcon = this.errorIcon;
            //            this.tf.errorIcon = this.errorIcon;
        }

        // setup name for submit
        this.el.dom.name = this.hiddenName || this.name || this.id;

        // prevent helper fields from being submitted
        this.sf.el.dom.removeAttribute("name");
        this.ef.el.dom.removeAttribute("name");

        // we're rendered flag
        this.isRendered = true;

        // update hidden field
        this.updateHidden();

    } // eo function onRender
    // }}}
    // {{{
    /**
    * @private
    */
    , adjustSize: Ext.BoxComponent.prototype.adjustSize
    // }}}
    // {{{
    /**
    * @private
    */
    , alignErrorIcon: function () {
        this.errorIcon.alignTo(this.tableEl, 'tl-tr', [2, 0]);
    }
    // }}}

    // {{{
    /**
    * Calls clearInvalid on the DateField and TimeField
    */
    , clearInvalid: function () {
        this.sf.clearInvalid();
        this.ef.clearInvalid();
    } // eo function clearInvalid
    // }}}
    // {{{
    /**
    * Calls markInvalid on both DateField and TimeField
    * @param {String} msg Invalid message to display
    */
    , markInvalid: function (msg) {
        this.sf.markInvalid(msg);
        this.ef.markInvalid(msg);
    } // eo function markInvalid
    // }}}
    // {{{
    /**
    * @private
    * called from Component::destroy. 
    * Destroys all elements and removes all listeners we've created.
    */
    , beforeDestroy: function () {
        if (this.isRendered) {
            //            this.removeAllListeners();
            this.wrap.removeAllListeners();
            this.wrap.remove();
            this.tableEl.remove();
            this.sf.destroy();
            this.ef.destroy();
        }
    } // eo function beforeDestroy
    // }}}
    // {{{
    /**
    * Disable this component.
    * @return {Ext.Component} this
    */
    , disable: function () {
        if (this.isRendered) {
            this.sf.disabled = this.disabled;
            this.sf.onDisable();
            this.ef.onDisable();
        }
        this.disabled = true;
        this.sf.disabled = true;
        this.ef.disabled = true;
        this.fireEvent("disable", this);
        return this;
    } // eo function disable
    // }}}
    // {{{
    /**
    * Enable this component.
    * @return {Ext.Component} this
    */
    , enable: function () {
        if (this.rendered) {
            this.sf.onEnable();
            this.ef.onEnable();
        }
        this.disabled = false;
        this.sf.disabled = false;
        this.ef.disabled = false;
        this.fireEvent("enable", this);
        return this;
    } // eo function enable
    // }}}
    // {{{
    /**
    * @private Focus date filed
    */
    , focus: function () {
        this.sf.focus();
    } // eo function focus
    // }}}
    // {{{
    /**
    * @private
    */
    , getPositionEl: function () {
        return this.wrap;
    }
    // }}}
    // {{{
    /**
    * @private
    */
    , getResizeEl: function () {
        return this.wrap;
    }
    // }}}
    // {{{
    /**
    * @return {Date/String} Returns value of this field
    */
    , getValue: function () {
        // create new instance of date
        var startValue = this.sf.getValue();
        if (startValue instanceof Date)
            startValue = Ext.util.Format.date(startValue, this.dateFormat || 'm/d/Y');
        if (!startValue)
            startValue = '';
        var endValue = this.ef.getValue();
        if (endValue instanceof Date)
            endValue = Ext.util.Format.date(endValue, this.dateFormat || 'm/d/Y');
        if (!endValue)
            endValue = '';

        return String.format('{0}{1}{2}', startValue, this.separator, endValue);
    } // eo function getValue
    // }}}
    // {{{
    /**
    * @return {Boolean} true = valid, false = invalid
    * @private Calls isValid methods of underlying DateField and TimeField and returns the result
    */
    , isValid: function () {
        return this.sf.isValid() && this.ef.isValid();
    } // eo function isValid
    // }}}
    // {{{
    /**
    * Returns true if this component is visible
    * @return {boolean} 
    */
    , isVisible: function () {
        return this.sf.rendered && this.sf.getActionEl().isVisible();
    } // eo function isVisible
    // }}}
    // {{{
    /** 
    * @private Handles blur event
    */
    , onBlur: function (f) {
        // called by both DateField and TimeField blur events

        // revert focus to previous field if clicked in between
        if (this.wrapClick) {
            f.focus();
            this.wrapClick = false;
        }

        this.updateHidden();

        // fire events later
        (function () {
            if (!this.sf.hasFocus && !this.ef.hasFocus) {
                var v = this.getValue();
                if (String(v) !== String(this.originalValue)) {
                    this.fireEvent("change", this, v, this.originalValue);
                }
                this.hasFocus = false;
                this.fireEvent('blur', this);
            }
        }).defer(100, this);

    } // eo function onBlur
    // }}}
    // {{{
    /**
    * @private Handles focus event
    */
    , onFocus: function () {
        if (!this.hasFocus) {
            this.hasFocus = true;
            this.originalValue = this.getValue();
            this.fireEvent("focus", this);
        }
    }
    // }}}
    // {{{
    /**
    * @private Just to prevent blur event when clicked in the middle of fields
    */
    , onMouseDown: function (e) {
        if (!this.disabled) {
            this.wrapClick = 'td' === e.target.nodeName.toLowerCase();
        }
    }
    // }}}
    // {{{
    /**
    * @private
    * Handles Tab and Shift-Tab events
    */
    , onSpecialKey: function (t, e) {
        var key = e.getKey();
        if (key === e.TAB) {
            if (t === this.sf && !e.shiftKey) {
                e.stopEvent();
                this.ef.focus();
            }
            if (t === this.ef && e.shiftKey) {
                e.stopEvent();
                this.sf.focus();
            }
        }
        // otherwise it misbehaves in editor grid
        if (key === e.ENTER) {
            this.updateValue();
        }

    } // eo function onSpecialKey
    // }}}
    // {{{
    /**
    * Resets the current field value to the originally loaded value 
    * and clears any validation messages. See Ext.form.BasicForm.trackResetOnLoad
    */
    , reset: function () {
        this.sf.reset();
        this.ef.reset();
    } // eo function reset
    // }}}

    // {{{
    /**
    * @private
    * Sets correct sizes of underlying DateField and TimeField
    * With workarounds for IE bugs
    */
    , setSize: function (w, h) {
        if (!w) {
            return;
        }

        fw = (w - 4) / 2;

        this.sf.setSize(fw, h);
        this.ef.setSize(fw, h);

        if (Ext.isIE) {
            this.sf.el.up('td').setWidth(fw);
            this.ef.el.up('td').setWidth(fw);
        }

    } // eo function setSize
    // }}}
    // {{{
    /**
    * @param {Mixed} val Value to set
    * Sets the value of this field
    */
    , setValue: function (val) {
        if (!val) {
            this.setValue('|');
            return;
        }

        // string.format to convert value to string, even if it is date time or number
        var da = (String.format('{0}', val)).split(this.separator);

        if (da[0] != '')
            this.sf.setValue(da[0]);
        if (da.length > 1 && da[1] != '')
            this.ef.setValue(da[1]);

        this.updateHidden();
    } // eo function setValue
    // }}}
    // {{{
    /**
    * Hide or show this component by boolean
    * @return {Ext.Component} this
    */
    , setVisible: function (visible) {
        if (visible) {
            this.sf.show();
            this.ef.show();
        } else {
            this.sf.hide();
            this.ef.hide();
        }
        return this;
    } // eo function setVisible
    // }}}
    //{{{
    , show: function () {
        return this.setVisible(true);
    } // eo function show
    //}}}
    //{{{
    , hide: function () {
        return this.setVisible(false);
    } // eo function hide
    //}}}
    // {{{
    /**
    * @private Updates the underlying hidden field value
    */
    , updateHidden: function () {
        if (this.isRendered) {
            this.el.dom.value = this.getValue();
        }
    }
    // }}}
    // {{{
    /**
    * @private Updates all of Date, Time and Hidden
    */
    , updateValue: function () {

        this.updateDate();
        this.updateTime();
        this.updateHidden();

        return;
    } // eo function updateValue
    // }}}
    // {{{
    /**
    * @return {Boolean} true = valid, false = invalid
    * calls validate methods of DateField and TimeField
    */
    , validate: function () {
        return this.sf.validate() && this.ef.validate();
    } // eo function validate
    // }}}
    // {{{
    /**
    * Returns renderer suitable to render this field
    * @param {Object} Column model config
    */
    , renderer: function (field) {
        var format = field.editor.dateFormat || Ext.ux.form.DateTime.prototype.dateFormat;
        format += ' ' + (field.editor.timeFormat || Ext.ux.form.DateTime.prototype.timeFormat);
        var renderer = function (val) {
            var retval = Ext.util.Format.date(val, format);
            return retval;
        };
        return renderer;
    }

});

Ext.reg('rangefield', WebLight.form.RangeField);
/// <reference path="../Core.js" />
/// <reference path="../ExtjsMappings.js" />

/**
* @class WebLight.grid.EditorColumnModel
* @extends Ext.grid.ColumnModel
*/
WebLight.grid.EditorColumnModel = Ext.extend(Ext.grid.ColumnModel, {

    _cachedEditor: null,
    constructor: function (config) {
        this._cachedEditor = new Ext.util.MixedCollection();

        WebLight.grid.EditorColumnModel.superclass.constructor.call(this, config);
    },

    /**
    * create a GridEditor with TextField
    * @param {Object} config field configuration
    * @return {Ext.grid.GridEditor}
    * @public
    */
    createTextEditor: function () {
        var config = { 'xtype': 'textfield' };
        if (arguments.length > 0 && Ext.isObject(arguments[0]))
            Ext.apply(config, arguments[0]);
        return this.createEditor(config);
    },

    createEditor: function (config) {
        if (!config || !Ext.isObject(config))
            return null;

        /// temp solution ,maybe has bug
        var encodeStore = function (store) {
            var count = 0;
            store.each(function (record) {
                if (record.id && record.id != NaN)
                    count += record.id + 1;
                else
                    count += encodeKey(record.data).length + 1;
            });
            return count;
        };

        var encodeKey = function (obj) {
            var a = [];

            if (Ext.isArray(obj)) {
                Ext.each(obj, function (item, index) {
                    a.push(encodeKey(item));
                });
            }
            else {
                for (key in obj) {
                    if (Ext.isString(obj[key]) || Ext.isNumber(obj[key]))
                        a.push(String.format('{0}={1}', key, obj[key]));
                    else if (key == "store") {
                        a.push(String.format('store={0}', encodeStore(obj[key])));
                    }
                }

            }
            return a.join('');
        };

        var cacheKey = encodeKey(config);
        var editor = this._cachedEditor.get(cacheKey);
        if (!editor) {
            editor = new Ext.grid.GridEditor(Ext.create(config));
            this._cachedEditor.add(cacheKey, editor);
        }
        return editor;
    },

    createNumberEditor: function () {
        var config = { 'xtype': 'numberfield', selectOnFocus: true, decimalPrecision: 2 };
        if (arguments.length > 0 && Ext.isObject(arguments[0]))
            Ext.apply(config, arguments[0]);
        return this.createEditor(config);
    },

    createNumberRangeEditor: function () {
        var config = { 'xtype': 'rangefield', fieldType: 'numberfield',
            dateFormat: 'm/d/Y',
            selectOnFocus: true,
            startConfig: { xtype: 'numberfield' },
            endConfig: { xtype: 'numberfield' }
        };
        if (arguments.length > 0 && Ext.isObject(arguments[0]))
            Ext.apply(config, arguments[0]);
        return this.createEditor(config);
    },


    createCheckbox: function () {
        var config = { 'xtype': 'checkbox' };
        if (arguments.length > 0 && Ext.isObject(arguments[0]))
            Ext.apply(config, arguments[0]);
        return this.createEditor(config);
    },

    createDateEditor: function () {
        var config = { 'xtype': 'datefield', format: 'm/d/Y', selectOnFocus: true };
        if (arguments.length > 0 && Ext.isObject(arguments[0]))
            Ext.apply(config, arguments[0]);
        return this.createEditor(config);
    },

    createDateRangeEditor: function () {
        /// fieldType did not use, just identify for cacheKey issue
        var config = { 'xtype': 'rangefield', fieldType:'datefield',
            dateFormat: 'm/d/Y',
            selectOnFocus: true,
            startConfig: { xtype: 'datefield' },
            endConfig: { xtype: 'datefield' }
        };
        if (arguments.length > 0 && Ext.isObject(arguments[0]))
            Ext.apply(config, arguments[0]);
        return this.createEditor(config);
    },

    createComboBox: function () {
        var config = { 'xtype': 'combo', mode: 'local',
            typeAhead: true,
            displayField: 'value',
            valueField: 'key',
            triggerAction: 'all',
            selectOnFocus: true
        };
        if (arguments.length > 0 && Ext.isObject(arguments[0]))
            Ext.apply(config, arguments[0]);
        return this.createEditor(config);
    },

    isCellEditable: function (colIndex, rowIndex) {
        var editor = this.getCellEditor(colIndex, rowIndex);
        return Ext.isDefined(editor) && null != editor;
    }

});

Ext.reg('combo', WebLight.form.ComboBox);
/// <reference path="../../references/jquery-1.4.1.js" />
/// <reference path="../../references/ext-jquery-adapter.js" />
/// <reference path="../../references/ext-all.js" />

/// <reference path="../Core.js" />

WebLight.grid.GridPanel = Ext.extend(Ext.grid.GridPanel, {

    pageSize: -1,

    enableRowNumberer: false,

    initComponent: function () {
        var current = this;
        // add pagingbar if user defined pageSize, default is -1
        if (this.pageSize != -1) {
            this.pagingBar = new Ext.PagingToolbar({
                store: this.store,
                displayInfo: true,
                pageSize: this.pageSize,
                prependButtons: true,
                items: this.bbar
            });
            this.bbar = this.pagingBar;
            if (this.store)
                this.store.setBaseParam(this.store.paramNames.limit, this.pageSize);
        }

        // enable persistent row number
        if (this.enableRowNumberer) {
            this.rowBaseIndex = 0;
            var columns = this.columns || this.cm.columns;
            columns = [{ dataIndex: '', header: "", width: 23, sortable: false, fixed: true, menuDisabled: true, renderer: function (v, p, record, rowIndex) { return current.rowBaseIndex + rowIndex + 1; } }].concat(columns);
            this.cm = new Ext.grid.ColumnModel({ columns: columns });
            if (this.store)
                this.store.on('beforeload', function (store, options) {
                    this.rowBaseIndex = options.params.start || 0;
                }, this);
        }

        this.addEvents('xcellclick');

        WebLight.grid.GridPanel.superclass.initComponent.call(this);


        this.on('cellclick', function (g, rowIndex, colIndex, e) {
            var target = e.getTarget();
            var command = $(target).attr('xcmd');
            if (command) {
                var record = g.getStore().getAt(rowIndex);
                this.fireEvent('xcellclick', this, command, record, rowIndex, colIndex, e);
            }
        }, this);

        /// link gridpanel and store if selectionmodel is singleselect
        if (this.getSelectionModel().singleSelect) {
            this.getSelectionModel().on('rowselect', function (model, rowIndex, r) {
                /// set store selectedIndex
                if (current.store && current.store.selectedIndex)
                    current.store.selectedIndex(current.store.indexOf(r));
            }, this);

            this.getSelectionModel().on('rowdeselect', function (model, rowIndex, r) {
                /// set store selectedIndex = -1
                if (current.store && current.store.selectedIndex && current.store.getAt(rowIndex) == model.getSelected())
                    current.store.selectedIndex(-1);
            }, this);
        }
    },
    getSelectedRecords: function () {
        if (this.disableSelection) {
            return [];
        }
        var selectionModel = this.getSelectionModel();
        if (!selectionModel.hasSelection())
            return [];
        return selectionModel.getSelections();
    },
    bindStore: function (store) {
        var current = this;
        var me = this;
        var view = me.getView();
        var applyStore = function (ds) {
            if (view.ds) {
                view.ds.un('load', view.onLoad, view);
                view.ds.un('datachanged', view.onDataChange, view);
                view.ds.un('add', view.onAdd, view);
                view.ds.un('remove', view.onRemove, view);
                view.ds.un('update', view.onUpdate, view);
                view.ds.un('clear', view.onClear, view);
                if (view.ds !== ds && view.ds.autoDestroy) {
                    view.ds.destroy();
                }
            }
            if (ds) {
                ds.on({
                    scope: view,
                    load: view.onLoad,
                    datachanged: view.onDataChange,
                    add: view.onAdd,
                    remove: view.onRemove,
                    update: view.onUpdate,
                    clear: view.onClear
                });
            }
            view.ds = ds;
        };

        if (store != this.store) {
            if (this.store && this.store.unbindControl) {
                this.store.unbindControl(current);
            }
            this.store = Ext.StoreMgr.lookup(store);
            if (this.store.bindControl)
                this.store.bindControl(current);

            this.store.on('beforeload', function (store, options) {
                this.rowBaseIndex = options.params.start || 0;
            }, this);

            if (this.pageSize != -1) {
                this.pagingBar.bindStore(store, true);
                this.store.setBaseParam(this.store.paramNames.limit, this.pageSize);
            }
            applyStore(store);
            this.fireEvent('storechanged', store);
        }
    },

    destroy: function () {
        var me = this;
        if (me.store && me.store.unbindControl) {
            me.store.unbindControl(me);
        }

        WebLight.grid.GridPanel.superclass.destroy.call(this);
    },

    exportToExcel: function () {
        var me = this;

        if (!me.store || !me.store.exportToExcel || me.store.getTotalCount() == 0)
            return;

        var configs = me.getColumnModel().getColumnsBy(function (columnConfig, index) {
            return !columnConfig.hidden && columnConfig.dataIndex && columnConfig.dataIndex.length > 0;
        });

        var columns = [];
        Ext.each(configs, function (item, index) {
            columns.push({ field: item.dataIndex, header: item.header });
        });

        //me.getEl().mask('Exporting...');
        me.store.exportToExcel(null, columns, String.format('{0}.csv', me.title));
        //me.getEl().unmask();
    }
});

