/*!
 * insert license here
 */
// vim: ts=4:sw=4:nu:fdc=4:nospell
/*global Ext */
/**
 * @singleton 
 * @class Ext.ux.util
 *
 * Contains utilities that do not fit elsewhere
 *
 * @author     Ing. Jozef Sakáloš
 * @copyright  (c) 2009, Ing. Jozef Sakáloš
 * @version    1.0
 * @date       30. January 2009
 * @revision   $Id: Ext.ux.util.js 620 2009-03-09 12:41:44Z jozo $
 *
 * @license
 * Ext.ux.util.js is licensed under the terms of
 * the Open Source LGPL 3.0 license.  Commercial use is permitted to the extent
 * that the code/component(s) do NOT become part of another Open Source or Commercially
 * licensed development library or toolkit without explicit permission.
 *
 * <p>License details: <a href="http://www.gnu.org/licenses/lgpl.html"
 * target="_blank">http://www.gnu.org/licenses/lgpl.html</a></p>
 *
 * @donate
 * <form action="https://www.paypal.com/cgi-bin/webscr" method="post" target="_blank">
 * <input type="hidden" name="cmd" value="_s-xclick">
 * <input type="hidden" name="hosted_button_id" value="3430419">
 * <input type="image" src="https://www.paypal.com/en_US/i/btn/x-click-butcc-donate.gif" 
 * border="0" name="submit" alt="PayPal - The safer, easier way to pay online.">
 * <img alt="" border="0" src="https://www.paypal.com/en_US/i/scr/pixel.gif" width="1" height="1">
 * </form>
 */

Ext.ns('Ext.ux.util');

// {{{
/**
 * @param {String} s
 * @return {String} MD5 sum
 * Calculates MD5 sum of the argument
 * @forum   28460
 * @author  <a href="http://extjs.com/forum/member.php?u=13648">wm003</a>
 * @version 1.0
 * @date    20. March 2008
 *
 */
Ext.ux.util.MD5 = function(s) {
    var hexcase = 0;
    var chrsz = 8;

    function safe_add(x, y){
        var lsw = (x & 0xFFFF) + (y & 0xFFFF);
        var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
        return (msw << 16) | (lsw & 0xFFFF);
    }
    function bit_rol(num, cnt){
        return (num << cnt) | (num >>> (32 - cnt));
    }
    function md5_cmn(q, a, b, x, s, t){
        return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s),b);
    }
    function md5_ff(a, b, c, d, x, s, t){
        return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t);
    }
    function md5_gg(a, b, c, d, x, s, t){
        return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t);
    }
    function md5_hh(a, b, c, d, x, s, t){
        return md5_cmn(b ^ c ^ d, a, b, x, s, t);
    }
    function md5_ii(a, b, c, d, x, s, t){
        return md5_cmn(c ^ (b | (~d)), a, b, x, s, t);
    }

    function core_md5(x, len){
        x[len >> 5] |= 0x80 << ((len) % 32);
        x[(((len + 64) >>> 9) << 4) + 14] = len;
        var a =  1732584193;
        var b = -271733879;
        var c = -1732584194;
        var d =  271733878;
        for(var i = 0; i < x.length; i += 16){
            var olda = a;
            var oldb = b;
            var oldc = c;
            var oldd = d;
            a = md5_ff(a, b, c, d, x[i+ 0], 7 , -680876936);
            d = md5_ff(d, a, b, c, x[i+ 1], 12, -389564586);
            c = md5_ff(c, d, a, b, x[i+ 2], 17,  606105819);
            b = md5_ff(b, c, d, a, x[i+ 3], 22, -1044525330);
            a = md5_ff(a, b, c, d, x[i+ 4], 7 , -176418897);
            d = md5_ff(d, a, b, c, x[i+ 5], 12,  1200080426);
            c = md5_ff(c, d, a, b, x[i+ 6], 17, -1473231341);
            b = md5_ff(b, c, d, a, x[i+ 7], 22, -45705983);
            a = md5_ff(a, b, c, d, x[i+ 8], 7 ,  1770035416);
            d = md5_ff(d, a, b, c, x[i+ 9], 12, -1958414417);
            c = md5_ff(c, d, a, b, x[i+10], 17, -42063);
            b = md5_ff(b, c, d, a, x[i+11], 22, -1990404162);
            a = md5_ff(a, b, c, d, x[i+12], 7 ,  1804603682);
            d = md5_ff(d, a, b, c, x[i+13], 12, -40341101);
            c = md5_ff(c, d, a, b, x[i+14], 17, -1502002290);
            b = md5_ff(b, c, d, a, x[i+15], 22,  1236535329);
            a = md5_gg(a, b, c, d, x[i+ 1], 5 , -165796510);
            d = md5_gg(d, a, b, c, x[i+ 6], 9 , -1069501632);
            c = md5_gg(c, d, a, b, x[i+11], 14,  643717713);
            b = md5_gg(b, c, d, a, x[i+ 0], 20, -373897302);
            a = md5_gg(a, b, c, d, x[i+ 5], 5 , -701558691);
            d = md5_gg(d, a, b, c, x[i+10], 9 ,  38016083);
            c = md5_gg(c, d, a, b, x[i+15], 14, -660478335);
            b = md5_gg(b, c, d, a, x[i+ 4], 20, -405537848);
            a = md5_gg(a, b, c, d, x[i+ 9], 5 ,  568446438);
            d = md5_gg(d, a, b, c, x[i+14], 9 , -1019803690);
            c = md5_gg(c, d, a, b, x[i+ 3], 14, -187363961);
            b = md5_gg(b, c, d, a, x[i+ 8], 20,  1163531501);
            a = md5_gg(a, b, c, d, x[i+13], 5 , -1444681467);
            d = md5_gg(d, a, b, c, x[i+ 2], 9 , -51403784);
            c = md5_gg(c, d, a, b, x[i+ 7], 14,  1735328473);
            b = md5_gg(b, c, d, a, x[i+12], 20, -1926607734);
            a = md5_hh(a, b, c, d, x[i+ 5], 4 , -378558);
            d = md5_hh(d, a, b, c, x[i+ 8], 11, -2022574463);
            c = md5_hh(c, d, a, b, x[i+11], 16,  1839030562);
            b = md5_hh(b, c, d, a, x[i+14], 23, -35309556);
            a = md5_hh(a, b, c, d, x[i+ 1], 4 , -1530992060);
            d = md5_hh(d, a, b, c, x[i+ 4], 11,  1272893353);
            c = md5_hh(c, d, a, b, x[i+ 7], 16, -155497632);
            b = md5_hh(b, c, d, a, x[i+10], 23, -1094730640);
            a = md5_hh(a, b, c, d, x[i+13], 4 ,  681279174);
            d = md5_hh(d, a, b, c, x[i+ 0], 11, -358537222);
            c = md5_hh(c, d, a, b, x[i+ 3], 16, -722521979);
            b = md5_hh(b, c, d, a, x[i+ 6], 23,  76029189);
            a = md5_hh(a, b, c, d, x[i+ 9], 4 , -640364487);
            d = md5_hh(d, a, b, c, x[i+12], 11, -421815835);
            c = md5_hh(c, d, a, b, x[i+15], 16,  530742520);
            b = md5_hh(b, c, d, a, x[i+ 2], 23, -995338651);
            a = md5_ii(a, b, c, d, x[i+ 0], 6 , -198630844);
            d = md5_ii(d, a, b, c, x[i+ 7], 10,  1126891415);
            c = md5_ii(c, d, a, b, x[i+14], 15, -1416354905);
            b = md5_ii(b, c, d, a, x[i+ 5], 21, -57434055);
            a = md5_ii(a, b, c, d, x[i+12], 6 ,  1700485571);
            d = md5_ii(d, a, b, c, x[i+ 3], 10, -1894986606);
            c = md5_ii(c, d, a, b, x[i+10], 15, -1051523);
            b = md5_ii(b, c, d, a, x[i+ 1], 21, -2054922799);
            a = md5_ii(a, b, c, d, x[i+ 8], 6 ,  1873313359);
            d = md5_ii(d, a, b, c, x[i+15], 10, -30611744);
            c = md5_ii(c, d, a, b, x[i+ 6], 15, -1560198380);
            b = md5_ii(b, c, d, a, x[i+13], 21,  1309151649);
            a = md5_ii(a, b, c, d, x[i+ 4], 6 , -145523070);
            d = md5_ii(d, a, b, c, x[i+11], 10, -1120210379);
            c = md5_ii(c, d, a, b, x[i+ 2], 15,  718787259);
            b = md5_ii(b, c, d, a, x[i+ 9], 21, -343485551);
            a = safe_add(a, olda);
            b = safe_add(b, oldb);
            c = safe_add(c, oldc);
            d = safe_add(d, oldd);
        }
        return [a, b, c, d];
    }
    function str2binl(str){
        var bin = [];
        var mask = (1 << chrsz) - 1;
        for(var i = 0; i < str.length * chrsz; i += chrsz) {
            bin[i>>5] |= (str.charCodeAt(i / chrsz) & mask) << (i%32);
        }
        return bin;
    }
    function binl2hex(binarray){
        var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
        var str = "";
        for(var i = 0; i < binarray.length * 4; i++) {
            str += hex_tab.charAt((binarray[i>>2] >> ((i%4)*8+4)) & 0xF) + hex_tab.charAt((binarray[i>>2] >> ((i%4)*8  )) & 0xF);
        }
        return str;
    }
    return binl2hex(core_md5(str2binl(s), s.length * chrsz));
};  
// }}}
// {{{
/**
 * Clone Function
 * @param {Object/Array} o Object or array to clone
 * @return {Object/Array} Deep clone of an object or an array
 * @author Ing. Jozef Sakáloš
 */
Ext.ux.util.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] = Ext.ux.util.clone(v);
			}
			else {
				c[p] = v;
			}
		}
	}
	return c;
}; // eo function clone
// }}}
// {{{
/**
 * Copies the source object properties with names that match target object properties to the target. 
 * Undefined properties of the source object are ignored even if names match.
 * This way it is possible to create a target object with defaults, apply source to it not overwriting 
 * target defaults with <code>undefined</code> values of source.
 * @param {Object} t The target object
 * @param {Object} s (optional) The source object. Equals to scope in which the function runs if omitted. That 
 * allows to set this function as method of any object and then call it in the scope of that object. E.g.:
 * <pre>
 * var p = new Ext.Panel({
 * &nbsp;	 prop1:11
 * &nbsp;	,prop2:22
 * &nbsp;	,<b>applyMatching:Ext.ux.util.applyMatching</b>
 * &nbsp;	// ...
 * });
 * var t = p.applyMatching({prop1:0, prop2:0, prop3:33});
 * </pre>
 * The resulting object:
 * <pre>
 * t = {prop1:11, prop2:22, prop3:33};
 * </pre>
 * @return {Object} Original passed target object with properties updated from source
 */
Ext.ux.util.applyMatching = function(t, s) {
	var s = s || this;
	for(var p in t) {
		if(t.hasOwnProperty(p) && undefined !== s[p]) {
			t[p] = s[p];
		}
	}
	return t;
}; // eo function applyMatching
// }}}

// conditional override
// {{{
/**
 * Same as {@link Ext#override} but overrides only if method does not exist in the target class
 * @member Ext
 * @param {Object} origclass
 * @param {Object} overrides
 */
Ext.overrideIf = 'function' === typeof Ext.overrideIf ? Ext.overrideIf : function(origclass, overrides) {
	if(overrides) {
		var p = origclass.prototype;
		for(var method in overrides) {
			if(!p[method]) {
				p[method] = overrides[method];
			}
		}
	}
};
// }}}

// RegExp
// {{{
/**
 * @class RegExp
 */
if('function' !== typeof RegExp.escape) {
	/**
	 * Escapes regular expression
	 * @param {String} s
	 * @return {String} The escaped string
	 * @static
	 */
	RegExp.escape = function(s) {
		if('string' !== typeof s) {
			return s;
		}
		return s.replace(/([.*+?\^=!:${}()|\[\]\/\\])/g, '\\$1');
	};
}
Ext.overrideIf(RegExp, {

	/**
	 * Clones RegExp object
	 * @return {RegExp} Clone of this RegExp
	 */
	 clone:function() {
		return new RegExp(this);
	} // eo function clone
});
// }}}

// Array
// {{{
Ext.overrideIf(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
	// }}}

});
// }}}

// eof
/*!
 * Ext JS Library 3.3.0
 * Copyright(c) 2006-2010 Ext JS, Inc.
 * licensing@extjs.com
 * http://www.extjs.com/license
 */
Ext.ns('Ext.ux.grid');

/**
 * @class Ext.ux.grid.CheckColumn
 * @extends Ext.grid.Column
 * <p>A Column subclass which renders a checkbox in each column cell which toggles the truthiness of the associated data field on click.</p>
 * <p><b>Note. As of ExtJS 3.3 this no longer has to be configured as a plugin of the GridPanel.</b></p>
 * <p>Example usage:</p>
 * <pre><code>
var cm = new Ext.grid.ColumnModel([{
       header: 'Foo',
       ...
    },{
       xtype: 'checkcolumn',
       header: 'Indoor?',
       dataIndex: 'indoor',
       width: 55
    }
]);

// create the grid
var grid = new Ext.grid.EditorGridPanel({
    ...
    colModel: cm,
    ...
});
 * </code></pre>
 * In addition to toggling a Boolean value within the record data, this
 * class toggles a css class between <tt>'x-grid3-check-col'</tt> and
 * <tt>'x-grid3-check-col-on'</tt> to alter the background image used for
 * a column.
 */
Ext.ux.grid.CheckColumn = Ext.extend(Ext.grid.Column, {

    /**
     * @private
     * Process and refire events routed from the GridView's processEvent method.
     */
    processEvent : function(name, e, grid, rowIndex, colIndex){
        if (name == 'mousedown') {
            var record = grid.store.getAt(rowIndex);
            record.set(this.dataIndex, !record.data[this.dataIndex]);
            return false; // Cancel row selection.
        } else {
            return Ext.grid.ActionColumn.superclass.processEvent.apply(this, arguments);
        }
    },

    renderer : function(v, p, record){
        p.css += ' x-grid3-check-col-td'; 
        return String.format('<div class="x-grid3-check-col{0}">&#160;</div>', v ? '-on' : '');
    },

    // Deprecate use as a plugin. Remove in 4.0
    init: Ext.emptyFn
});

// register ptype. Deprecate. Remove in 4.0
Ext.preg('checkcolumn', Ext.ux.grid.CheckColumn);

// backwards compat. Remove in 4.0
Ext.grid.CheckColumn = Ext.ux.grid.CheckColumn;

// register Column xtype
Ext.grid.Column.types.checkcolumn = Ext.ux.grid.CheckColumn;/**
 * @class Ext.ux.form.ColorPickerFieldPlus
 * @extends Ext.form.TwinTriggerField
 * This class makes Ext.ux.ColorPicker and Ext.ux.form.ColorPickerField available as a form field.
 * @license: IYUIYBMAC (If you use it, you buy me a Coke)
 * @author of Ext.ux.form.ColorPickerField: Robert B. Williams (extjs id: vtswingkid)
 * @author of Ext.ux.form.ColorPickerFieldPlus: Ramiro P. Saenz
 * @constructor
 * Creates a new ColorPickerFieldPlus
 * @param {Object} config Configuration options
 * @version 1.0
 * @date 14/04/09 (dd/mm/yy)
 */

Ext.namespace("Ext.ux", "Ext.ux.menu", "Ext.ux.form");

Ext.ux.menu.ColorMenu = Ext.extend(Ext.menu.Menu, {
    enableScrolling : false,
    hideOnClick     : true,
    initComponent : function(){
        Ext.apply(this, {
            plain         : true,
            showSeparator : false,
            items: this.picker = new Ext.ux.ColorPicker(Ext.apply({
                style: 'width:350px;'
            }, this.initialConfig))
        });
        Ext.ux.menu.ColorMenu.superclass.initComponent.call(this);
        this.relayEvents(this.picker, ['select']);
        this.on('select', this.menuHide, this);
        if (this.handler) {
            this.on('select', this.handler, this.scope || this)
        }
    },
    menuHide: function(){
        if (this.hideOnClick) {
            this.hide(true);
        }
    }
});


Ext.form.VTypes["hexText"] = "Invalid Hex code, eg: (#F0F0F0)";
Ext.form.VTypes["hex"] = function(v){ return /^#?([a-f]|[A-F]|[0-9]){3}(([a-f]|[A-F]|[0-9]){3})?$/.test(v); };

Ext.ux.form.ColorPickerFieldPlus = Ext.extend(Ext.form.TwinTriggerField,  {
    /**
     * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-trigger")
     */
    trigger1Class: 'x-form-color-trigger-1',
    /**
     * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-trigger")
     */
    trigger2Class: 'x-form-color-trigger-2',
    /**
     * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-image")
     */
    hideTrigger1: false,
    /**
     * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-image")
     */
    hideTrigger2: false,
    // private
    vtype: 'hex',
    // private
    initComponent : function(){
        Ext.ux.form.ColorPickerFieldPlus.superclass.initComponent.call(this);
        this.menu1 = new Ext.menu.ColorMenu();
        this.menu2 = new Ext.ux.menu.ColorMenu();
        this.on('valid', function(){
            var v = this.getValue();
            var i = this.menu2.picker.rgbToHex(this.menu2.picker.invert(this.menu2.picker.hexToRgb(v)));
            if (v && i) {
                this.el.applyStyles('background: #' + v + '; color: #' + i + ';');
            }
        }, this);
    },
    setValue : function(v){     
        if (typeof(v) != 'string') {
            v = parseInt(v);
            v = this.menu2.picker.rgbToHex( (v&0x00ff0000) >> 16 ,(v&0x00ff00) >> 8,(v&0x00ff) );
        }
        Ext.ux.form.ColorPickerFieldPlus.superclass.setValue.apply(this, [v]);
//      var i = this.menu2.picker.rgbToHex(this.menu2.picker.invert(this.menu2.picker.hexToRgb(v)));
//      if (v && i) {
//          this.el.applyStyles('background: #' + v + '; color: #' + i + ';');
//      }
    },  
    onDestroy : function(){
        if(this.menu2) {
            this.menu2.destroy();
        }
        if(this.wrap){
            this.wrap.remove();
        }
        Ext.ux.form.ColorPickerFieldPlus.superclass.onDestroy.call(this);
    },
//    onBlur : function(){
//        Ext.ux.form.ColorPickerFieldPlus.superclass.onBlur.call(this);
//      var v = this.getValue();
//      var i = this.menu2.picker.rgbToHex(this.menu2.picker.invert(this.menu2.picker.hexToRgb(v)));
//      this.el.applyStyles('background: #'+v+'; color: #'+i+';');
//    },
    menu1Listeners : {
        select: function(m, c){
            var v0 = this.getValue();
            this.setValue(c);
            var v1 = this.getValue();
            if (v1 !== v0) {
                this.fireEvent('change', this, v1, v0);
            }
            this.focus.defer(10, this);
        },
        show : function(m){ // retain focus styling
            this.onFocus();
        },
        hide : function(){
            this.focus.defer(10, this);
            var ml = this.menu1Listeners;
            this.menu1.un("select", ml.select,  this);
            this.menu1.un("show", ml.show,  this);
            this.menu1.un("hide", ml.hide,  this);
        }
    },
    menu2Listeners : {
        select: function(m, c){
            var v0 = this.getValue();
            this.setValue(c);
            var v1 = this.getValue();
            if (v1 !== v0) {
                this.fireEvent('change', this, v1, v0);
            }
            this.focus.defer(10, this);
        },
        show : function(m){ // retain focus styling
            this.onFocus();
        },
        hide : function(){
            this.focus.defer(10, this);
            var ml = this.menu2Listeners;
            this.menu2.un("select", ml.select,  this);
            this.menu2.un("show", ml.show,  this);
            this.menu2.un("hide", ml.hide,  this);
        }
    },
    onTrigger1Click : function(){
        if(this.disabled){
            return;
        }
        this.menu1.on(Ext.apply({}, this.menu1Listeners, {
            scope:this
        }));

        this.menu1.show(this.el, "tl-bl?");
    },
    onTrigger2Click : function(){
        if(this.disabled){
            return;
        }
        this.menu2.on(Ext.apply({}, this.menu2Listeners, {
           scope:this
        }));
        this.menu2.show(this.el, "tl-bl?");
        this.menu2.picker.setColor(this.getValue());
    }
});
Ext.reg('colorpickerfieldplus', Ext.ux.form.ColorPickerFieldPlus);
/**
 * @class Ext.ux.ColorPicker
 * @extends Ext.BoxComponent
 * This is a color picker.
 * @license: LGPLv3
 * @author: Amon
 * @constructor
 * Creates a new ColorPicker
 * @param {Object} config Configuration options
 * @version 1.1.2
 */

Ext.namespace( 'Ext.ux' );
/**
 *
 */
Ext.ux.ColorPicker = Ext.extend( Ext.BoxComponent, {
	/**
	 *
	 */
	initComponent: function() {
		this.applyDefaultsCP();
		Ext.ux.ColorPicker.superclass.initComponent.apply( this, arguments );
		this.addEvents('select');
	},
	/**
	 *
	 */
	onRender: function() {
		Ext.ux.ColorPicker.superclass.onRender.apply( this, arguments );
		// check if container, self-container or renderTo exists
		this.body = this.body || ( this.container || ( this.renderTo || Ext.DomHelper.append( Ext.getBody(), {}, true ) ) );
		if( !this.el ) {
			this.el = this.body;
			if( this.cls ) { Ext.get( this.el ).addClass( this.cls ); }
		}
		// render this component
		this.renderComponent();
	},
	/**
	 *
	 */
	applyDefaultsCP: function() {
		Ext.apply( this, {
			'cls': 'x-cp-mainpanel',
			'resizable': this.resizable || false,
			'HSV': {
				h: 0,
				s: 0,
				v: 0
			},
			updateMode: null
		});
	},
	/**
	 *
	 */
	renderComponent: function() {
		// create RGB Slider
		Ext.DomHelper.append( this.body, {
			'id': this.cpGetId( 'rgb' ),
			'cls': 'x-cp-rgbpicker'
		});
		// Create HUE Slider
		Ext.DomHelper.append( this.body, {
			'id': this.cpGetId( 'hue' ),
			'cls': 'x-cp-huepicker'
		});
		// Initialize HUE Picker DD
		this.huePicker = Ext.DomHelper.append( this.body, { 'cls': 'x-cp-hueslider' });
		this.hueDD = new Ext.dd.DD( this.huePicker, 'huePicker' );
		this.hueDD.constrainTo( this.cpGetId( 'hue' ), {'top':-7,'right':-3,'bottom':-7,'left':-3} );
		this.hueDD.onDrag = this.moveHuePicker.createDelegate( this );
		// initialize onclick on the rgb picker
		Ext.get( this.cpGetId( 'hue' ) ).on( 'mousedown', this.clickHUEPicker.createDelegate( this ) );
		// initialize start position
		Ext.get( this.huePicker ).moveTo( Ext.get( this.cpGetId( 'hue' ) ).getLeft() - 3, Ext.get( this.cpGetId( 'hue' ) ).getTop() - 7 );
		// Initialize RGB Picker DD
		this.rgbPicker = Ext.DomHelper.append( this.body, { 'cls': 'x-cp-rgbslider' });
		this.rgbDD = new Ext.dd.DD( this.rgbPicker, 'rgbPicker' );
		this.rgbDD.constrainTo( this.cpGetId( 'rgb' ), -7 );
		this.rgbDD.onDrag = this.moveRGBPicker.createDelegate( this );
		// initialize onclick on the rgb picker
		Ext.get( this.cpGetId( 'rgb' ) ).on( 'mousedown', this.clickRGBPicker.createDelegate( this ) );
		// initialize start position
		Ext.get( this.rgbPicker ).moveTo( Ext.get( this.cpGetId( 'rgb' ) ).getLeft() - 7, Ext.get( this.cpGetId( 'rgb' ) ).getTop() - 7 );
		// Create color divs and Form elements
		this.formPanel = new Ext.form.FormPanel({
			'renderTo': Ext.DomHelper.append( this.body, {
							'id': this.cpGetId( 'fCont' ),
							'cls': 'x-cp-formcontainer'
						}, true ),
			'frame': true,
			'labelAlign': 'left',
			'labelWidth': 10,
			'baseCls': 'x-panel-mc',
			'items': [{
				'layout': 'column',
				'items': [{
					'columnWidth': .5,
					'layout': 'form',
					'defaultType': 'numberfield',
					'defaults': {
						'width': 30,
						'value': 0,
						'minValue': 0,
						'maxValue': 255,
						'allowBlank': true,
						'labelSeparator': ''
					},
					'items': [{
						'fieldLabel': 'R',
						'id': this.cpGetId( 'iRed' )
					},{
						'fieldLabel': 'G',
						'id': this.cpGetId( 'iGreen' )
					},{
						'fieldLabel': 'B',
						'id': this.cpGetId( 'iBlue' )
					}]
				},{
					'columnWidth': .5,
					'layout': 'form',
					'defaultType': 'numberfield',
					'defaults': {
						'width': 30,
						'value': 0,
						'minValue': 0,
						'maxValue': 255,
						'allowBlank': true,
						'labelSeparator': ''
					},
					'items': [{
						'fieldLabel': 'H',
						'maxValue': 360,
						'id': this.cpGetId( 'iHue' )
					},{
						'fieldLabel': 'S',
						'id': this.cpGetId( 'iSat' )
					},{
						'fieldLabel': 'V',
						'id': this.cpGetId( 'iVal' )
					}]
				}]
			},{
				'layout': 'form',
				'defaultType': 'textfield',
				'labelAlign': 'left',
				'defaults': {
					'width': 85,
					'value': '000000',
					'labelSeparator': '',
					'allowBlank': true
				},
				'id': this.cpGetId( 'cCont' ),
				'items': [{
					'fieldLabel': '#',
					'id': this.cpGetId( 'iHexa' ),
					'value': '000000'
				}]
			}]
		});
		Ext.getCmp( this.cpGetId( 'iRed' ) ).on( 'change', this.updateFromIRGB.createDelegate( this ) );
		Ext.getCmp( this.cpGetId( 'iGreen' ) ).on( 'change', this.updateFromIRGB.createDelegate( this ) );
		Ext.getCmp( this.cpGetId( 'iBlue' ) ).on( 'change', this.updateFromIRGB.createDelegate( this ) );
		Ext.getCmp( this.cpGetId( 'iHue' ) ).on( 'change', this.updateFromIHSV.createDelegate( this ) );
		Ext.getCmp( this.cpGetId( 'iSat' ) ).on( 'change', this.updateFromIHSV.createDelegate( this ) );
		Ext.getCmp( this.cpGetId( 'iVal' ) ).on( 'change', this.updateFromIHSV.createDelegate( this ) );
		Ext.getCmp( this.cpGetId( 'iHexa' ) ).on( 'change', this.updateFromIHexa.createDelegate( this ) );
		Ext.DomHelper.append( this.cpGetId( 'cCont' ), { 'cls': 'x-cp-colorbox', 'id': this.cpGetId( 'cWebSafe' ) }, true ).update( 'Websafe' );
		Ext.DomHelper.append( this.cpGetId( 'cCont' ), { 'cls': 'x-cp-colorbox', 'id': this.cpGetId( 'cInverse' ) }, true ).update( 'Inverse' );
		Ext.DomHelper.append( this.cpGetId( 'cCont' ), { 'cls': 'x-cp-colorbox', 'id': this.cpGetId( 'cColor' ) }, true ).update( 'Pick Color' );
		Ext.get( this.cpGetId( 'cWebSafe' ) ).on( 'click', this.updateFromBox.createDelegate( this ) );
		Ext.get( this.cpGetId( 'cInverse' ) ).on( 'click', this.updateFromBox.createDelegate( this ) );
		Ext.get( this.cpGetId( 'cColor' ) ).on( 'click', this.selectColor.createDelegate( this ) );
		Ext.DomHelper.append( this.body, {'tag':'br','cls':'x-cp-clearfloat'});
	},
	/**
	 *
	 */
	cpGetId: function( postfix ) {
		return this.getId() + '__' + ( postfix || 'cp' );
	},
	/**
	 *
	 */
	updateRGBPosition: function( x, y ) {
		this.updateMode = 'click';
		x = x < 0 ? 0 : x;
		x = x > 181 ? 181 : x;
		y = y < 0 ? 0 : y;
		y = y > 181 ? 181 : y;
		this.HSV.s = this.getSaturation( x );
		this.HSV.v = this.getValue( y );
		Ext.get( this.rgbPicker ).moveTo( Ext.get( this.cpGetId( 'rgb' ) ).getLeft() + x - 7, Ext.get( this.cpGetId( 'rgb' ) ).getTop() + y - 7, ( this.animateMove || true ) );
		this.updateColor();
	},
	/**
	 *
	 */
	updateHUEPosition: function( y ) {
		this.updateMode = 'click';
		y = y < 1 ? 1 : y;
		y = y > 181 ? 181 : y;
		this.HSV.h = Math.round( 360 / 181 * ( 181 - y ) );
		Ext.get( this.huePicker ).moveTo( Ext.get( this.huePicker ).getLeft(), Ext.get( this.cpGetId( 'hue' ) ).getTop() + y - 7, ( this.animateMove || true ) );
		this.updateRGBPicker( this.HSV.h );
		this.updateColor();
	},
	/**
	 *
	 */
	clickRGBPicker: function( event, element ) {
		this.updateRGBPosition( event.xy[0] - Ext.get( this.cpGetId( 'rgb' ) ).getLeft() , event.xy[1] - Ext.get( this.cpGetId( 'rgb' ) ).getTop() );
	},
	/**
	 *
	 */
	clickHUEPicker: function( event, element ) {
		this.updateHUEPosition( event.xy[1] - Ext.get( this.cpGetId( 'hue' ) ).getTop() );
	},
	/**
	 *
	 */
	moveRGBPicker: function( event ) {
		this.rgbDD.constrainTo( this.cpGetId( 'rgb' ), -7 );
		this.updateRGBPosition( Ext.get( this.rgbPicker ).getLeft() - Ext.get( this.cpGetId( 'rgb' ) ).getLeft() + 7 , Ext.get( this.rgbPicker ).getTop() - Ext.get( this.cpGetId( 'rgb' ) ).getTop() + 7 );
	},
	/**
	 *
	 */
	moveHuePicker: function( event ) {
		this.hueDD.constrainTo( this.cpGetId( 'hue' ), {'top':-7,'right':-3,'bottom':-7,'left':-3} );
		this.updateHUEPosition( Ext.get( this.huePicker ).getTop() - Ext.get( this.cpGetId( 'hue' ) ).getTop() + 7 );
	},
	/**
	 *
	 */
	updateRGBPicker: function( newValue ) {
		this.updateMode = 'click';
		Ext.get( this.cpGetId( 'rgb' ) ).setStyle({ 'background-color': '#' + this.rgbToHex( this.hsvToRgb( newValue, 1, 1 ) ) });
		this.updateColor();
	},
	/**
	 *
	 */
	updateColor: function() {
		var rgb = this.hsvToRgb( this.HSV.h, this.HSV.s, this.HSV.v );
		var websafe = this.websafe( rgb );
		var invert = this.invert( rgb );
		var wsInvert = this.invert( websafe );
		if( this.updateMode !== 'hexa' ) {
			Ext.getCmp( this.cpGetId( 'iHexa' ) ).setValue( this.rgbToHex( rgb ) );
		}
		if( this.updateMode !== 'rgb' ) {
			Ext.getCmp( this.cpGetId( 'iRed' ) ).setValue( rgb[0] );
			Ext.getCmp( this.cpGetId( 'iGreen' ) ).setValue( rgb[1] );
			Ext.getCmp( this.cpGetId( 'iBlue' ) ).setValue( rgb[2] );
		}
		if( this.updateMode !== 'hsv' ) {
			Ext.getCmp( this.cpGetId( 'iHue' ) ).setValue( Math.round( this.HSV.h ) );
			Ext.getCmp( this.cpGetId( 'iSat' ) ).setValue( Math.round( this.HSV.s * 100 ) );
			Ext.getCmp( this.cpGetId( 'iVal' ) ).setValue( Math.round( this.HSV.v * 100 ) );
		}
		Ext.get( this.cpGetId( 'cColor' ) ).setStyle({
			'background': '#' + this.rgbToHex( rgb ),
			'color': '#' + this.rgbToHex( invert )
		});
		Ext.getDom( this.cpGetId( 'cColor' ) ).title = '#'+this.rgbToHex( rgb );
		Ext.get( this.cpGetId( 'cInverse' ) ).setStyle({
			'background': '#' + this.rgbToHex( invert ),
			'color': '#' + this.rgbToHex( rgb )
		});
		Ext.getDom( this.cpGetId( 'cInverse' ) ).title = '#'+this.rgbToHex( invert );
		Ext.get( this.cpGetId( 'cWebSafe' ) ).setStyle({
			'background': '#' + this.rgbToHex( websafe ),
			'color': '#' + this.rgbToHex( wsInvert )
		});
		Ext.getDom( this.cpGetId( 'cWebSafe' ) ).title = '#'+this.rgbToHex( websafe );
		Ext.get( this.huePicker ).moveTo( Ext.get( this.huePicker ).getLeft(), Ext.get( this.cpGetId( 'hue' ) ).getTop() + this.getHPos( Ext.getCmp( this.cpGetId( 'iHue' ) ).getValue() ) - 7, ( this.animateMove || true ) );
		Ext.get( this.rgbPicker ).moveTo( Ext.get( this.cpGetId( 'rgb' ) ).getLeft() + this.getSPos( Ext.getCmp( this.cpGetId( 'iSat' ) ).getValue() / 100 ) - 7, Ext.get( this.cpGetId( 'hue' ) ).getTop() + this.getVPos( Ext.getCmp( this.cpGetId( 'iVal' ) ).getValue() / 100 ) - 7, ( this.animateMove || true ) );
		Ext.get( this.cpGetId( 'rgb' ) ).setStyle({ 'background-color': '#' + this.rgbToHex( this.hsvToRgb( Ext.getCmp( this.cpGetId( 'iHue' ) ).getValue(), 1, 1 ) ) });
	},
	/**
	 *
	 */
	setColor: function(c) {
		if(!/^[0-9a-fA-F]{6}$/.test(c))return;
		Ext.getCmp( this.cpGetId( 'iHexa' ) ).setValue(c);
		this.updateFromIHexa();
	},
	/**
	 *
	 */
	updateFromIRGB: function( input, newValue, oldValue ) {
		this.updateMode = 'rgb';
		var temp = this.rgbToHsv( Ext.getCmp( this.cpGetId( 'iRed' ) ).getValue(), Ext.getCmp( this.cpGetId( 'iGreen' ) ).getValue(), Ext.getCmp( this.cpGetId( 'iBlue' ) ).getValue() );
		this.HSV = { h: temp[0], s:temp[1], v:temp[2]};
		this.updateColor();
	},
	/**
	 *
	 */
	updateFromIHSV: function( input, newValue, oldValue ) {
		this.updateMode = 'hsv';
		this.HSV = { h: Ext.getCmp( this.cpGetId( 'iHue' ) ).getValue(), s:Ext.getCmp( this.cpGetId( 'iSat' ) ).getValue() / 100, v:Ext.getCmp( this.cpGetId( 'iVal' ) ).getValue() / 100};
		this.updateColor();
	},
	/**
	 *
	 */
	updateFromIHexa: function( input, newValue, oldValue ) {
		this.updateMode = 'hexa';
		var temp = this.rgbToHsv( this.hexToRgb( Ext.getCmp( this.cpGetId( 'iHexa' ) ).getValue() ) );
		this.HSV = { h: temp[0], s:temp[1], v:temp[2]};
		this.updateColor();
	},
	/**
	 *
	 */
	updateFromBox: function( event, element ) {
		this.updateMode = 'click';
		var temp = this.rgbToHsv( this.hexToRgb( Ext.get( element ).getColor( 'backgroundColor', '', '' ) ) );
		this.HSV = { h: temp[0], s:temp[1], v:temp[2]};
		this.updateColor();
	},

	selectColor: function( event, element ) {
		this.fireEvent('select', this, Ext.get( element ).getColor( 'backgroundColor', '', '' ));
	},
	/**
	 * Convert HSV color format to RGB color format
	 * @param {Integer/Array( h, s, v )} h
	 * @param {Integer} s (optional)
	 * @param {Integer} v (optional)
	 * @return {Array}
	 */
	hsvToRgb: function( h, s, v ) {
		if( h instanceof Array ) { return this.hsvToRgb.call( this, h[0], h[1], h[2] ); }
		var r, g, b, i, f, p, q, t;
	    i = Math.floor( ( h / 60 ) % 6 );
	    f = ( h / 60 ) - i;
	    p = v * ( 1 - s );
	    q = v * ( 1 - f * s );
	    t = v * ( 1 - ( 1 - f ) * s );
	    switch(i) {
	        case 0: r=v; g=t; b=p; break;
	        case 1: r=q; g=v; b=p; break;
	        case 2: r=p; g=v; b=t; break;
	        case 3: r=p; g=q; b=v; break;
	        case 4: r=t; g=p; b=v; break;
	        case 5: r=v; g=p; b=q; break;
	    }
	    return [this.realToDec( r ), this.realToDec( g ), this.realToDec( b )];
	},
	/**
	 * Convert RGB color format to HSV color format
	 * @param {Integer/Array( r, g, b )} r
	 * @param {Integer} g (optional)
	 * @param {Integer} b (optional)
	 * @return {Array}
	 */
	rgbToHsv: function( r, g, b ) {
		if( r instanceof Array ) { return this.rgbToHsv.call( this, r[0], r[1], r[2] ); }
        r = r / 255;
        g = g / 255;
        b = b / 255;
        var min, max, delta, h, s, v;
        min = Math.min( Math.min( r, g ), b );
        max = Math.max( Math.max( r, g ), b );
        delta = max - min;
        switch (max) {
            case min: h = 0; break;
            case r:   h = 60 * ( g - b ) / delta;
                      if ( g < b ) { h += 360; }
                      break;
            case g:   h = ( 60 * ( b - r ) / delta ) + 120; break;
            case b:   h = ( 60 * ( r - g ) / delta ) + 240; break;
        }
        s = ( max === 0 ) ? 0 : 1 - ( min / max );
        return [Math.round( h ), s, max];
	},
	/**
	 * Convert a float to decimal
	 * @param {Float} n
	 * @return {Integer}
	 */
	realToDec: function( n ) {
		return Math.min( 255, Math.round( n * 256 ) );
	},
	/**
	 * Convert RGB color format to Hexa color format
	 * @param {Integer/Array( r, g, b )} r
	 * @param {Integer} g (optional)
	 * @param {Integer} b (optional)
	 * @return {String}
	 */
	rgbToHex: function( r, g, b ) {
		if( r instanceof Array ) { return this.rgbToHex.call( this, r[0], r[1], r[2] ); }
		return this.decToHex( r ) + this.decToHex( g ) + this.decToHex( b );
	},
	/**
	 * Convert an integer to hexa
	 * @param {Integer} n
	 * @return {String}
	 */
	decToHex: function( n ) {
		var HCHARS = '0123456789ABCDEF';
        n = parseInt(n, 10);
        n = ( !isNaN( n )) ? n : 0;
        n = (n > 255 || n < 0) ? 0 : n;
        return HCHARS.charAt( ( n - n % 16 ) / 16 ) + HCHARS.charAt( n % 16 );
	},
	/**
	 * Return with position of a character in this.HCHARS string
	 * @private
	 * @param {Char} c
	 * @return {Integer}
	 */
	getHCharPos: function( c ) {
		if(c != undefined){
			var HCHARS = '0123456789ABCDEF';
			return HCHARS.indexOf( c.toUpperCase() );
		}
	},
	/**
	 * Convert a hexa string to decimal
	 * @param {String} hex
	 * @return {Integer}
	 */
	hexToDec: function( hex ) {
        var s = hex.split('');
        return ( ( this.getHCharPos( s[0] ) * 16 ) + this.getHCharPos( s[1] ) );
	},
	/**
	 * Convert a hexa string to RGB color format
	 * @param {String} hex
	 * @return {Array}
	 */
	hexToRgb: function( hex ) {
		return [ this.hexToDec( hex.substr(0, 2) ), this.hexToDec( hex.substr(2, 2) ), this.hexToDec( hex.substr(4, 2) ) ];
	},
	/**
	 * Convert Y coordinate to HUE value
	 * @private
	 * @param {Integer} y
	 * @return {Integer}
	 */
	getHue: function( y ) {
		var hue = 360 - Math.round( ( ( 181 - y ) / 181 ) * 360 );
		return hue === 360 ? 0 : hue;
	},
	/**
	 * Convert HUE value to Y coordinate
	 * @private
	 * @param {Integer} hue
	 * @return {Integer}
	 */
	getHPos: function( hue ) {
		return 181 - hue * ( 181 / 360 );
	},
	/**
	 * Convert X coordinate to Saturation value
	 * @private
	 * @param {Integer} x
	 * @return {Integer}
	 */
	getSaturation: function( x ) {
		return x / 181;
	},
	/**
	 * Convert Saturation value to Y coordinate
	 * @private
	 * @param {Integer} saturation
	 * @return {Integer}
	 */
	getSPos: function( saturation ) {
		return saturation * 181;
	},
	/**
	 * Convert Y coordinate to Brightness value
	 * @private
	 * @param {Integer} y
	 * @return {Integer}
	 */
	getValue: function( y ) {
		return ( 181 - y ) / 181;
	},
	/**
	 * Convert Brightness value to Y coordinate
	 * @private
	 * @param {Integer} value
	 * @return {Integer}
	 */
	getVPos: function( value ) {
		return 181 - ( value * 181 );
	},
	/**
	 * Not documented yet
	 */
	checkSafeNumber: function( v ) {
	    if ( !isNaN( v ) ) {
	        v = Math.min( Math.max( 0, v ), 255 );
	        var i, next;
	        for( i=0; i<256; i=i+51 ) {
	            next = i + 51;
	            if ( v>=i && v<=next ) { return ( v - i > 25 ) ? next : i; }
	        }
	    }
	    return v;
	},
	/**
	 * Not documented yet
	 */
	websafe: function( r, g, b ) {
		if( r instanceof Array ) { return this.websafe.call( this, r[0], r[1], r[2] ); }
		return [this.checkSafeNumber( r ), this.checkSafeNumber( g ), this.checkSafeNumber( b )];
	},
	/**
	 * Not documented yet
	 */
	invert: function( r, g, b ) {
		if( r instanceof Array ) { return this.invert.call( this, r[0], r[1], r[2] ); }
		return [255-r,255-g,255-b];
	}
});
/**
 *
 */
Ext.ux.ColorDialog = Ext.extend( Ext.Window, {
	initComponent: function() {
		this.width = ( !this.width || this.width < 353 ) ? 353 : this.width;
		this.applyDefaultsCP();
		Ext.ux.ColorDialog.superclass.initComponent.apply( this, arguments );
	},
	onRender: function() {
		Ext.ux.ColorDialog.superclass.onRender.apply( this, arguments );
		this.renderComponent();
	}
});
Ext.applyIf( Ext.ux.ColorDialog.prototype, Ext.ux.ColorPicker.prototype );
/**
 *
 */
Ext.ux.ColorPanel = Ext.extend( Ext.Panel, {
	initComponent: function() {
		this.width = ( !this.width || this.width < 300 ) ? 300 : this.width;
		this.applyDefaultsCP();
		Ext.ux.ColorPanel.superclass.initComponent.apply( this, arguments );
	},
	onRender: function() {
		Ext.ux.ColorPanel.superclass.onRender.apply( this, arguments );
		this.renderComponent();
	}
});
Ext.applyIf( Ext.ux.ColorPanel.prototype, Ext.ux.ColorPicker.prototype );
/**
 * Register Color* for Lazy Rendering
 */
Ext.reg( 'colorpicker', Ext.ux.ColorPicker );
Ext.reg( 'colordialog', Ext.ux.ColorDialog );
Ext.reg( 'colorpanel', Ext.ux.ColorPanel );// vim: ts=4:sw=4:nu:fdc=4:nospell
/*global Ext */
/**
 * @class Ext.ux.grid.Search
 * @extends Ext.util.Observable
 *
 * Search plugin for Ext.grid.GridPanel, Ext.grid.EditorGrid ver. 2.x or subclasses of them
 *
 * @author    Ing. Jozef Sakáloš
 * @copyright (c) 2008, by Ing. Jozef Sakáloš
 * @date      <ul>
 * <li>17. January 2008<li>
 * <li>6. February 2009</li>
 * </ul>
 * @version   1.1.1
 * @revision  $Id: Ext.ux.grid.Search.js 798 2010-01-17 00:46:57Z jozo $
 *
 * @license Ext.ux.grid.Search is licensed under the terms of
 * the Open Source LGPL 3.0 license.  Commercial use is permitted to the extent
 * that the code/component(s) do NOT become part of another Open Source or Commercially
 * licensed development library or toolkit without explicit permission.
 * 
 * <p>License details: <a href="http://www.gnu.org/licenses/lgpl.html"
 * target="_blank">http://www.gnu.org/licenses/lgpl.html</a></p>
 *
 * @forum     23615
 * @demo      http://gridsearch.extjs.eu
 * @download  
 * <ul>
 * <li><a href="http://gridsearch.extjs.eu/gridsearch.tar.bz2">gridsearch.tar.bz2</a></li>
 * <li><a href="http://gridsearch.extjs.eu/gridsearch.tar.gz">gridsearch.tar.gz</a></li>
 * <li><a href="http://gridsearch.extjs.eu/gridsearch.zip">gridsearch.zip</a></li>
 * </ul>
 *
 * @donate
 * <form action="https://www.paypal.com/cgi-bin/webscr" method="post" target="_blank">
 * <input type="hidden" name="cmd" value="_s-xclick">
 * <input type="hidden" name="hosted_button_id" value="3430419">
 * <input type="image" src="https://www.paypal.com/en_US/i/btn/x-click-butcc-donate.gif" 
 * border="0" name="submit" alt="PayPal - The safer, easier way to pay online.">
 * <img alt="" border="0" src="https://www.paypal.com/en_US/i/scr/pixel.gif" width="1" height="1">
 * </form>
 */

Ext.ns('Ext.ux.grid');

// Check RegExp.escape dependency
if('function' !== typeof RegExp.escape) {
	throw('RegExp.escape function is missing. Include Ext.ux.util.js file.');
}

/**
 * Creates new Search plugin
 * @constructor
 * @param {Object} A config object
 */
Ext.ux.grid.Search = function(config) {
	Ext.apply(this, config);
	Ext.ux.grid.Search.superclass.constructor.call(this);
}; // eo constructor

Ext.extend(Ext.ux.grid.Search, Ext.util.Observable, {
	/**
	 * @cfg {Boolean} autoFocus Try to focus the input field on each store load if set to true (defaults to undefined)
	 */

	/**
	 * @cfg {String} searchText Text to display on menu button
	 */
	 searchText:'Search'

	/**
	 * @cfg {String} searchTipText Text to display as input tooltip. Set to '' for no tooltip
	 */ 
	,searchTipText:'Type a text to search and press Enter'

	/**
	 * @cfg {String} selectAllText Text to display on menu item that selects all fields
	 */
	,selectAllText:'Select All'

	/**
	 * @cfg {String} position Where to display the search controls. Valid values are top and bottom
	 * Corresponding toolbar has to exist at least with mimimum configuration tbar:[] for position:top or bbar:[]
	 * for position bottom. Plugin does NOT create any toolbar.(defaults to "bottom")
	 */
	,position:'bottom'

	/**
	 * @cfg {String} iconCls Icon class for menu button (defaults to "icon-magnifier")
	 */
	,iconCls:'icon-magnifier'

	/**
	 * @cfg {String/Array} checkIndexes Which indexes to check by default. Can be either 'all' for all indexes
	 * or array of dataIndex names, e.g. ['persFirstName', 'persLastName'] (defaults to "all")
	 */
	,checkIndexes:'all'

	/**
	 * @cfg {Array} disableIndexes Array of index names to disable (not show in the menu), e.g. ['persTitle', 'persTitle2']
	 * (defaults to [] - empty array)
	 */
	,disableIndexes:[]

	/**
	 * Field containing search text (read-only)
	 * @property field
	 * @type {Ext.form.TwinTriggerField}
	 */

	/**
	 * @cfg {String} dateFormat How to format date values. If undefined (the default) 
	 * date is formatted as configured in colummn model
	 */

	/**
	 * @cfg {Boolean} showSelectAll Select All item is shown in menu if true (defaults to true)
	 */
	,showSelectAll:true

	/**
	 * Menu containing the column module fields menu with checkboxes (read-only)
	 * @property menu
	 * @type {Ext.menu.Menu}
	 */

	/**
	 * @cfg {String} menuStyle Valid values are 'checkbox' and 'radio'. If menuStyle is radio
	 * then only one field can be searched at a time and selectAll is automatically switched off. 
	 * (defaults to "checkbox")
	 */
	,menuStyle:'checkbox'

	/**
	 * @cfg {Number} minChars Minimum characters to type before the request is made. If undefined (the default)
	 * the trigger field shows magnifier icon and you need to click it or press enter for search to start. If it
	 * is defined and greater than 0 then maginfier is not shown and search starts after minChars are typed.
	 * (defaults to undefined)
	 */

	/**
	 * @cfg {String} minCharsTipText Tooltip to display if minChars is > 1
	 */
	,minCharsTipText:'Type at least {0} characters'

	/**
	 * @cfg {String} mode Use 'remote' for remote stores or 'local' for local stores. If mode is local
	 * no data requests are sent to server the grid's store is filtered instead (defaults to "remote")
	 */
	,mode:'remote'

	/**
	 * @cfg {Array} readonlyIndexes Array of index names to disable (show in menu disabled), e.g. ['persTitle', 'persTitle2']
	 * (defaults to undefined)
	 */

	/**
	 * @cfg {Number} width Width of input field in pixels (defaults to 100)
	 */
	,width:100

	/**
	 * @cfg {String} xtype xtype is usually not used to instantiate this plugin but you have a chance to identify it
	 */
	,xtype:'gridsearch'

	/**
	 * @cfg {Object} paramNames Params name map (defaults to {fields:"fields", query:"query"}
	 */
	,paramNames: {
		 fields:'fields'
		,query:'query'
	}

	/**
	 * @cfg {String} shortcutKey Key to fucus the input field (defaults to r = Sea_r_ch). Empty string disables shortcut
	 */
	,shortcutKey:'r'

	/**
	 * @cfg {String} shortcutModifier Modifier for shortcutKey. Valid values: alt, ctrl, shift (defaults to "alt")
	 */
	,shortcutModifier:'alt'

	/**
	 * @cfg {String} align "left" or "right" (defaults to "left")
	 */

	/**
	 * @cfg {Number} minLength Force user to type this many character before he can make a search 
	 * (defaults to undefined)
	 */

	/**
	 * @cfg {Ext.Panel/String} toolbarContainer Panel (or id of the panel) which contains toolbar we want to render
	 * search controls to (defaults to this.grid, the grid this plugin is plugged-in into)
	 */
	
	// {{{
	/**
	 * @private
	 * @param {Ext.grid.GridPanel/Ext.grid.EditorGrid} grid reference to grid this plugin is used for
	 */
	,init:function(grid) {
		this.grid = grid;

		// setup toolbar container if id was given
		if('string' === typeof this.toolbarContainer) {
			this.toolbarContainer = Ext.getCmp(this.toolbarContainer);
		}

		// do our processing after grid render and reconfigure
		grid.onRender = grid.onRender.createSequence(this.onRender, this);
		grid.reconfigure = grid.reconfigure.createSequence(this.reconfigure, this);
	} // eo function init
	// }}}
	// {{{
	/**
	 * adds plugin controls to <b>existing</b> toolbar and calls reconfigure
	 * @private
	 */
	,onRender:function() {
		var panel = this.toolbarContainer || this.grid;
		var tb = 'bottom' === this.position ? panel.bottomToolbar : panel.topToolbar;
		// add menu
		this.menu = new Ext.menu.Menu();

		// handle position
		if('right' === this.align) {
			tb.addFill();
		}
		else {
			if(0 < tb.items.getCount()) {
				tb.addSeparator();
			}
		}

		// add menu button
		tb.add({
			 text:this.searchText
			,menu:this.menu
			,iconCls:this.iconCls
		});

		// add input field (TwinTriggerField in fact)
		this.field = new Ext.form.TwinTriggerField({
			 width:this.width
			,selectOnFocus:undefined === this.selectOnFocus ? true : this.selectOnFocus
			,trigger1Class:'x-form-clear-trigger'
			,trigger2Class:this.minChars ? 'x-hide-display' : 'x-form-search-trigger'
			,onTrigger1Click:this.onTriggerClear.createDelegate(this)
			,onTrigger2Click:this.minChars ? Ext.emptyFn : this.onTriggerSearch.createDelegate(this)
			,minLength:this.minLength
		});

		// install event handlers on input field
		this.field.on('render', function() {
			// register quick tip on the way to search
			if((undefined === this.minChars || 1 < this.minChars) && this.minCharsTipText) {
				Ext.QuickTips.register({
					 target:this.field.el
					,text:this.minChars ? String.format(this.minCharsTipText, this.minChars) : this.searchTipText
				});
			}

			if(this.minChars) {
				this.field.el.on({scope:this, buffer:300, keyup:this.onKeyUp});
			}

			// install key map
			var map = new Ext.KeyMap(this.field.el, [{
				 key:Ext.EventObject.ENTER
				,scope:this
				,fn:this.onTriggerSearch
			},{
				 key:Ext.EventObject.ESC
				,scope:this
				,fn:this.onTriggerClear
			}]);
			map.stopEvent = true;
		}, this, {single:true});

		tb.add(this.field);

		// re-layout the panel if the toolbar is outside
		if(panel !== this.grid) {
			this.toolbarContainer.doLayout();
		}

		// reconfigure
		this.reconfigure();

		// keyMap
		if(this.shortcutKey && this.shortcutModifier) {
			var shortcutEl = this.grid.getEl();
			var shortcutCfg = [{
				 key:this.shortcutKey
				,scope:this
				,stopEvent:true
				,fn:function() {
					this.field.focus();
				}
			}];
			shortcutCfg[0][this.shortcutModifier] = true;
			this.keymap = new Ext.KeyMap(shortcutEl, shortcutCfg);
		}

		if(true === this.autoFocus) {
			this.grid.store.on({scope:this, load:function(){this.field.focus();}});
		}

	} // eo function onRender
	// }}}
	// {{{
	/**
	 * field el keypup event handler. Triggers the search
	 * @private
	 */
	,onKeyUp:function(e, t, o) {

		// ignore special keys 
		if(e.isNavKeyPress()) {
			return;
		}

		var length = this.field.getValue().toString().length;
		if(0 === length || this.minChars <= length) {
			this.onTriggerSearch();
		}
	} // eo function onKeyUp
	// }}}
	// {{{
	/**
	 * Clear Trigger click handler
	 * @private 
	 */
	,onTriggerClear:function() {
		if(this.field.getValue()) {
			this.field.setValue('');
			this.field.focus();
			this.onTriggerSearch();
		}
	} // eo function onTriggerClear
	// }}}
	// {{{
	/**
	 * Search Trigger click handler (executes the search, local or remote)
	 * @private 
	 */
	,onTriggerSearch:function() {
		if(!this.field.isValid()) {
			return;
		}
		var val = this.field.getValue();
		var store = this.grid.store;

		// grid's store filter
		if('local' === this.mode) {
			store.clearFilter();
			if(val) {
				store.filterBy(function(r) {
					var retval = false;
					this.menu.items.each(function(item) {
						if(!item.checked || retval) {
							return;
						}
						var rv = r.get(item.dataIndex);
						rv = rv instanceof Date ? rv.format(this.dateFormat || r.fields.get(item.dataIndex).dateFormat) : rv;
						var re = new RegExp(RegExp.escape(val), 'gi');
						retval = re.test(rv);
					}, this);
					if(retval) {
						return true;
					}
					return retval;
				}, this);
			}
			else {
			}
		}
		// ask server to filter records
		else {
			// clear start (necessary if we have paging)
			if(store.lastOptions && store.lastOptions.params) {
				store.lastOptions.params[store.paramNames.start] = 0;
			}

			// get fields to search array
			var fields = [];
			this.menu.items.each(function(item) {
				if(item.checked) {
					fields.push(item.dataIndex);
				}
			});

			// add fields and query to baseParams of store
			delete(store.baseParams[this.paramNames.fields]);
			delete(store.baseParams[this.paramNames.query]);
			if (store.lastOptions && store.lastOptions.params) {
				delete(store.lastOptions.params[this.paramNames.fields]);
				delete(store.lastOptions.params[this.paramNames.query]);
			}
			if(fields.length) {
				store.baseParams[this.paramNames.fields] = Ext.encode(fields);
				store.baseParams[this.paramNames.query] = val;
			}

			// reload store
			store.reload();
		}

	} // eo function onTriggerSearch
	// }}}
	// {{{
	/**
	 * @param {Boolean} true to disable search (TwinTriggerField), false to enable
	 */
	,setDisabled:function() {
		this.field.setDisabled.apply(this.field, arguments);
	} // eo function setDisabled
	// }}}
	// {{{
	/**
	 * Enable search (TwinTriggerField)
	 */
	,enable:function() {
		this.setDisabled(false);
	} // eo function enable
	// }}}
	// {{{
	/**
	 * Disable search (TwinTriggerField)
	 */
	,disable:function() {
		this.setDisabled(true);
	} // eo function disable
	// }}}
	// {{{
	/**
	 * (re)configures the plugin, creates menu items from column model
	 * @private 
	 */
	,reconfigure:function() {

		// {{{
		// remove old items
		var menu = this.menu;
		menu.removeAll();

		// add Select All item plus separator
		if(this.showSelectAll && 'radio' !== this.menuStyle) {
			menu.add(new Ext.menu.CheckItem({
				 text:this.selectAllText
				,checked:!(this.checkIndexes instanceof Array)
				,hideOnClick:false
				,handler:function(item) {
					var checked = ! item.checked;
					item.parentMenu.items.each(function(i) {
						if(item !== i && i.setChecked && !i.disabled) {
							i.setChecked(checked);
						}
					});
				}
			}),'-');
		}

		// }}}
		// {{{
		// add new items
		var cm = this.grid.colModel;
		var group = undefined;
		if('radio' === this.menuStyle) {
			group = 'g' + (new Date).getTime();	
		}
		Ext.each(cm.config, function(config) {
			var disable = false;
			if(config.header && config.dataIndex) {
				Ext.each(this.disableIndexes, function(item) {
					disable = disable ? disable : item === config.dataIndex;
				});
				if(!disable) {
					menu.add(new Ext.menu.CheckItem({
						 text:config.header
						,hideOnClick:false
						,group:group
						,checked:'all' === this.checkIndexes
						,dataIndex:config.dataIndex
					}));
				}
			}
		}, this);
		// }}}
		// {{{
		// check items
		if(this.checkIndexes instanceof Array) {
			Ext.each(this.checkIndexes, function(di) {
				var item = menu.items.find(function(itm) {
					return itm.dataIndex === di;
				});
				if(item) {
					item.setChecked(true, true);
				}
			}, this);
		}
		// }}}
		// {{{
		// disable items
		if(this.readonlyIndexes instanceof Array) {
			Ext.each(this.readonlyIndexes, function(di) {
				var item = menu.items.find(function(itm) {
					return itm.dataIndex === di;
				});
				if(item) {
					item.disable();
				}
			}, this);
		}
		// }}}

	} // eo function reconfigure
	// }}}

}); // eo extend

// eof
(function(){Ext.namespace("Ext.ux");var c=false;var b;var a;Ext.ux.TinyMCE=Ext.extend(Ext.form.Field,{tinymceSettings:null,allowBlank:true,invalidText:"The value in this field is invalid",invalidClass:"invalid-content-body",minLengthText:"The minimum length for this field is {0}",maxLengthText:"The maximum length for this field is {0}",blankText:"This field is required",hideMode:"offsets",defaultAutoCreate:{tag:"textarea",style:"width:1px;height:1px;",autocomplete:"off"},constructor:function(d){var e={tinymceSettings:{accessibility_focus:false}};Ext.apply(e,d);this.addEvents({editorcreated:true});Ext.ux.TinyMCE.superclass.constructor.call(this,e)},initComponent:function(){this.tinymceSettings=this.tinymceSettings||{};Ext.ux.TinyMCE.initTinyMCE({language:this.tinymceSettings.language})},initEvents:function(){this.originalValue=this.getValue()},onRender:function(e,d){Ext.ux.TinyMCE.superclass.onRender.call(this,e,d);if(Ext.type(this.width)=="number"){this.tinymceSettings.width=this.width}if(Ext.type(this.height)=="number"){this.tinymceSettings.height=this.height}this.el.dom.setAttribute("tabIndex",-1);this.el.addClass("x-hidden");this.textareaEl=this.el;var h={overflow:"hidden"};if(Ext.isIE){h["margin-top"]="-1px";h["margin-bottom"]="-1px"}this.wrapEl=this.el.wrap({style:h});this.actionMode="wrapEl";this.positionEl=this.wrapEl;var i=this.getId();this.ed=new tinymce.Editor(i,this.tinymceSettings);var f=new Ext.util.DelayedTask(this.validate,this);this.ed.onKeyPress.add(function(k,j){f.delay(250)}.createDelegate(this));this.ed.onBeforeRenderUI.add(function(k,j){k.controlManager=new a(this,k)}.createDelegate(this));this.ed.onPostRender.add(function(k,j){var m=k.settings;var n=Ext.get(Ext.DomQuery.selectNode("#"+this.ed.id+"_tbl td.mceToolbar"));if(n!=null){var l=n.select("> table.mceToolbar");Ext.DomHelper.append(n,{tag:"div",id:this.ed.id+"_xtbar",style:{overflow:"hidden"}},true).appendChild(l)}k.windowManager=new b({editor:this.ed,manager:this.manager});Ext.get(k.getContentAreaContainer()).addClass("patch-content-body");Ext.Element.fly(m.content_editable?k.getBody():k.getWin()).on("focus",this.onFocus,this);Ext.Element.fly(m.content_editable?k.getBody():k.getWin()).on("blur",this.onBlur,this,this.inEditor&&Ext.isWindows&&Ext.isGecko?{buffer:10}:null)}.createDelegate(this));this.ed.onChange.add(function(k,j){this.fireEvent("change",k,j)}.createDelegate(this));this.ed.render();tinyMCE.add(this.ed);(function g(){if(!this.isVisible()){arguments.callee.defer(50,this);return}var j=this.getSize();this.withEd(function(){this._setEditorSize(j.width,j.height);this.fireEvent("editorcreated")})}).call(this)},getResizeEl:function(){return this.wrapEl},getName:function(){return this.rendered&&this.textareaEl.dom.name?this.textareaEl.dom.name:(this.name||"")},initValue:function(){if(!this.rendered){Ext.ux.TinyMCE.superclass.initValue.call(this)}else{if(this.value!==undefined){this.setValue(this.value)}else{var d=this.textareaEl.value;if(d){this.setValue(d)}}}},beforeDestroy:function(){if(this.ed){tinyMCE.remove(this.ed)}if(this.wrapEl){Ext.destroy(this.wrapEl)}Ext.ux.TinyMCE.superclass.beforeDestroy.call(this)},getRawValue:function(){if(!this.rendered||!this.ed.initialized){return Ext.value(this.value,"")}var d=this.ed.getContent();if(d===this.emptyText){d=""}return d},getValue:function(){if(!this.rendered||!this.ed.initialized){return Ext.value(this.value,"")}var d=this.ed.getContent();if(d===this.emptyText||d===undefined){d=""}return d},setRawValue:function(d){this.value=d;if(this.rendered){this.withEd(function(){this.ed.undoManager.clear();this.ed.setContent(d===null||d===undefined?"":d);this.ed.startContent=this.ed.getContent({format:"raw"})})}},setValue:function(d){this.value=d;if(this.rendered){this.withEd(function(){this.ed.undoManager.clear();this.ed.setContent(d===null||d===undefined?"":d);this.ed.startContent=this.ed.getContent({format:"raw"});this.validate()})}},isDirty:function(){if(this.disabled||!this.rendered){return false}return this.ed&&this.ed.initialized&&this.ed.isDirty()},syncValue:function(){if(this.rendered&&this.ed.initialized){this.ed.save()}},getEd:function(){return this.ed},disable:function(){this.withEd(function(){var d=this.ed.getBody();d=Ext.get(d);if(d.hasClass("mceContentBody")){d.removeClass("mceContentBody");d.addClass("mceNonEditable")}})},enable:function(){this.withEd(function(){var d=this.ed.getBody();d=Ext.get(d);if(d.hasClass("mceNonEditable")){d.removeClass("mceNonEditable");d.addClass("mceContentBody")}})},onResize:function(e,d){if(Ext.type(e)!="number"){e=this.getWidth()}if(Ext.type(d)!="number"){d=this.getHeight()}if(e==0||d==0){return}if(this.rendered&&this.isVisible()){this.withEd(function(){this._setEditorSize(e,d)})}},_setEditorSize:function(d,m){if(!this.ed.theme.AdvancedTheme){return}if(d<100){d=100}if(m<129){m=129}var f=Ext.get(this.ed.id+"_tbl"),i=Ext.get(this.ed.id+"_ifr"),e=Ext.get(this.ed.id+"_xtbar");var o=d;if(f){o=d-f.getFrameWidth("lr")}var j=0;if(e){j=e.getHeight();var l=e.findParent("td",5,true);j+=l.getFrameWidth("tb");e.setWidth(o)}var h=f.child(".mceStatusbar");var n=0;if(h){n+=h.getHeight()}var g=m-j-n;var k=i.findParent("td",5,true);if(k){g-=k.getFrameWidth("tb")}f.setSize(d,m);i.setSize(o,g)},focus:function(e,d){if(d){this.focus.defer(typeof d=="number"?d:10,this,[e,false]);return}this.withEd(function(){this.ed.focus()});return this},processValue:function(d){return Ext.util.Format.stripTags(d)},validateValue:function(d){if(Ext.isFunction(this.validator)){var f=this.validator(d);if(f!==true){this.markInvalid(f);return false}}if(d.length<1||d===this.emptyText){if(this.allowBlank){this.clearInvalid();return true}else{this.markInvalid(this.blankText);return false}}if(d.length<this.minLength){this.markInvalid(String.format(this.minLengthText,this.minLength));return false}if(d.length>this.maxLength){this.markInvalid(String.format(this.maxLengthText,this.maxLength));return false}if(this.vtype){var e=Ext.form.VTypes;if(!e[this.vtype](d,this)){this.markInvalid(this.vtypeText||e[this.vtype+"Text"]);return false}}if(this.regex&&!this.regex.test(d)){this.markInvalid(this.regexText);return false}return true},withEd:function(d){if(!this.ed){this.on("editorcreated",function(){this.withEd(d)},this)}else{if(this.ed.initialized){d.call(this)}else{this.ed.onInit.add(function(){d.defer(10,this)}.createDelegate(this))}}}});Ext.apply(Ext.ux.TinyMCE,{tinymcePlugins:"pagebreak,style,layer,table,advhr,advimage,advlink,emotions,iespell,insertdatetime,preview,media,searchreplace,print,contextmenu,paste,directionality,noneditable,visualchars,nonbreaking,xhtmlxtras,template",initTinyMCE:function(e){if(!c){b=Ext.extend(tinymce.WindowManager,{constructor:function(f){b.superclass.constructor.call(this,f.editor);this.manager=f.manager},alert:function(g,f,h){Ext.MessageBox.alert("",g,function(){if(!Ext.isEmpty(f)){f.call(this)}},h)},confirm:function(g,f,h){Ext.MessageBox.confirm("",g,function(i){if(!Ext.isEmpty(f)){f.call(this,i=="yes")}},h)},open:function(f,h){f=f||{};h=h||{};if(!f.type){this.bookmark=this.editor.selection.getBookmark("simple")}f.width=parseInt(f.width||320);f.height=parseInt(f.height||240)+(tinymce.isIE?8:0);f.min_width=parseInt(f.min_width||150);f.min_height=parseInt(f.min_height||100);f.max_width=parseInt(f.max_width||2000);f.max_height=parseInt(f.max_height||2000);f.movable=true;f.resizable=true;h.mce_width=f.width;h.mce_height=f.height;h.mce_inline=true;this.features=f;this.params=h;var g=new Ext.Window({title:f.name,width:f.width,height:f.height,minWidth:f.min_width,minHeight:f.min_height,resizable:true,maximizable:f.maximizable,minimizable:f.minimizable,modal:true,stateful:false,constrain:true,manager:this.manager,layout:"fit",items:[new Ext.BoxComponent({autoEl:{tag:"iframe",src:f.url||f.file},style:"border-width: 0px;"})]});h.mce_window_id=g.getId();g.show(null,function(){if(f.left&&f.top){g.setPagePosition(f.left,f.top)}var i=g.getPosition();f.left=i[0];f.top=i[1];this.onOpen.dispatch(this,f,h)},this);return g},close:function(g){if(!g.tinyMCEPopup||!g.tinyMCEPopup.id){b.superclass.close.call(this,g);return}var f=Ext.getCmp(g.tinyMCEPopup.id);if(f){this.onClose.dispatch(this);f.close()}},setTitle:function(h,g){if(!h.tinyMCEPopup||!h.tinyMCEPopup.id){b.superclass.setTitle.call(this,h,g);return}var f=Ext.getCmp(h.tinyMCEPopup.id);if(f){f.setTitle(g)}},resizeBy:function(g,i,j){var f=Ext.getCmp(j);if(f){var h=f.getSize();f.setSize(h.width+g,h.height+i)}},focus:function(g){var f=Ext.getCmp(g);if(f){f.setActive(true)}}});a=Ext.extend(tinymce.ControlManager,{control:null,constructor:function(h,f,g){this.control=h;a.superclass.constructor.call(this,f,g)},createDropMenu:function(i,g){var f=a.superclass.createDropMenu.call(this,i,g);var h=f.showMenu;f.showMenu=function(j,l,k){h.call(this,j,l,k);Ext.fly("menu_"+this.id).setStyle("z-index",200001)};return f},createColorSplitButton:function(i,g){var f=a.superclass.createColorSplitButton.call(this,i,g);var h=f.showMenu;f.showMenu=function(j,l,k){h.call(this,j,l,k);Ext.fly(this.id+"_menu").setStyle("z-index",200001)};return f}});var d={mode:"none",plugins:Ext.ux.TinyMCE.tinymcePlugins,theme:"advanced"};Ext.apply(d,e);if(!tinymce.dom.Event.domLoaded){tinymce.dom.Event._pageInit()}tinyMCE.init(d);c=true}}});Ext.ComponentMgr.registerType("tinymce",Ext.ux.TinyMCE)})();/*!
 * Ext JS Library 3.2.1
 * Copyright(c) 2006-2010 Ext JS, Inc.
 * licensing@extjs.com
 * http://www.extjs.com/license
 */
Ext.ns('Ext.ux.form');

/**
 * @class Ext.ux.form.FileUploadField
 * @extends Ext.form.TextField
 * Creates a file upload field.
 * @xtype fileuploadfield
 */
Ext.ux.form.FileUploadField = Ext.extend(Ext.form.TextField,  {
    /**
     * @cfg {String} buttonText The button text to display on the upload button (defaults to
     * 'Browse...').  Note that if you supply a value for {@link #buttonCfg}, the buttonCfg.text
     * value will be used instead if available.
     */
    buttonText: 'Browse...',
    /**
     * @cfg {Boolean} buttonOnly True to display the file upload field as a button with no visible
     * text field (defaults to false).  If true, all inherited TextField members will still be available.
     */
    buttonOnly: false,
    /**
     * @cfg {Number} buttonOffset The number of pixels of space reserved between the button and the text field
     * (defaults to 3).  Note that this only applies if {@link #buttonOnly} = false.
     */
    buttonOffset: 3,
    /**
     * @cfg {Object} buttonCfg A standard {@link Ext.Button} config object.
     */

    // private
    readOnly: true,

    /**
     * @hide
     * @method autoSize
     */
    autoSize: Ext.emptyFn,

    // private
    initComponent: function(){
        Ext.ux.form.FileUploadField.superclass.initComponent.call(this);

        this.addEvents(
            /**
             * @event fileselected
             * Fires when the underlying file input field's value has changed from the user
             * selecting a new file from the system file selection dialog.
             * @param {Ext.ux.form.FileUploadField} this
             * @param {String} value The file value returned by the underlying file input field
             */
            'fileselected'
        );
    },

    // private
    onRender : function(ct, position){
        Ext.ux.form.FileUploadField.superclass.onRender.call(this, ct, position);

        this.wrap = this.el.wrap({cls:'x-form-field-wrap x-form-file-wrap'});
        this.el.addClass('x-form-file-text');
        this.el.dom.removeAttribute('name');
        this.createFileInput();

        var btnCfg = Ext.applyIf(this.buttonCfg || {}, {
            text: this.buttonText
        });
        this.button = new Ext.Button(Ext.apply(btnCfg, {
            renderTo: this.wrap,
            cls: 'x-form-file-btn' + (btnCfg.iconCls ? ' x-btn-icon' : '')
        }));

        if(this.buttonOnly){
            this.el.hide();
            this.wrap.setWidth(this.button.getEl().getWidth());
        }

        this.bindListeners();
        this.resizeEl = this.positionEl = this.wrap;
    },
    
    bindListeners: function(){
        this.fileInput.on({
            scope: this,
            mouseenter: function() {
                this.button.addClass(['x-btn-over','x-btn-focus'])
            },
            mouseleave: function(){
                this.button.removeClass(['x-btn-over','x-btn-focus','x-btn-click'])
            },
            mousedown: function(){
                this.button.addClass('x-btn-click')
            },
            mouseup: function(){
                this.button.removeClass(['x-btn-over','x-btn-focus','x-btn-click'])
            },
            change: function(){
                var v = this.fileInput.dom.value;
                this.setValue(v);
                this.fireEvent('fileselected', this, v);    
            }
        }); 
    },
    
    createFileInput : function() {
        this.fileInput = this.wrap.createChild({
            id: this.getFileInputId(),
            name: this.name||this.getId(),
            cls: 'x-form-file',
            tag: 'input',
            type: 'file',
            size: 1
        });
    },
    
    reset : function(){
        this.fileInput.remove();
        this.createFileInput();
        this.bindListeners();
        Ext.ux.form.FileUploadField.superclass.reset.call(this);
    },

    // private
    getFileInputId: function(){
        return this.id + '-file';
    },

    // private
    onResize : function(w, h){
        Ext.ux.form.FileUploadField.superclass.onResize.call(this, w, h);

        this.wrap.setWidth(w);

        if(!this.buttonOnly){
            var w = this.wrap.getWidth() - this.button.getEl().getWidth() - this.buttonOffset;
            this.el.setWidth(w);
        }
    },

    // private
    onDestroy: function(){
        Ext.ux.form.FileUploadField.superclass.onDestroy.call(this);
        Ext.destroy(this.fileInput, this.button, this.wrap);
    },
    
    onDisable: function(){
        Ext.ux.form.FileUploadField.superclass.onDisable.call(this);
        this.doDisable(true);
    },
    
    onEnable: function(){
        Ext.ux.form.FileUploadField.superclass.onEnable.call(this);
        this.doDisable(false);

    },
    
    // private
    doDisable: function(disabled){
        this.fileInput.dom.disabled = disabled;
        this.button.setDisabled(disabled);
    },


    // private
    preFocus : Ext.emptyFn,

    // private
    alignErrorIcon : function(){
        this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
    }

});

Ext.reg('fileuploadfield', Ext.ux.form.FileUploadField);

// backwards compat
Ext.form.FileUploadField = Ext.ux.form.FileUploadField;
/**
 * @class Ext.ux.form.Recaptcha
 * @extends Ext.BoxComponent
 * Recaptcha field.
 * @constructor
 * Creates a new Recaptcha field
 * @param {Ext.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
 * element and its id used as the component id.  If a string is passed, it is assumed to be the id of an existing element
 * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
 *
 * More information can be found about reCAPTCHA and lib files at: http://recaptcha.net
 */
Ext.ux.Recaptcha = Ext.extend(Ext.BoxComponent, {
    /**
     * @cfg {String} publickey The key to generate your recaptcha
     */
    /**
     * @cfg {String} theme The name of the theme
     */
    onRender : function(ct, position){
        if(!this.el){
            this.el = document.createElement('div');
            this.el.id = this.getId();
                        Recaptcha.create(this.publickey, this.el, {
                                theme: this.theme,
                                lang: this.lang,
                                callback: Recaptcha.focus_response_field
                        });
        }
        Ext.ux.Recaptcha.superclass.onRender.call(this, ct, position);
    }
});
Ext.reg('recaptcha', Ext.ux.Recaptcha);
/*
 * Ext JS Library 2.2
 * Copyright(c) 2006-2008, Ext JS, LLC.
 * licensing@extjs.com
 * 
 * http://extjs.com/license
 */

Ext.grid.RowExpander = function(config){
    Ext.apply(this, config);

    this.addEvents({
        beforeexpand : true,
        expand: true,
        beforecollapse: true,
        collapse: true
    });

    Ext.grid.RowExpander.superclass.constructor.call(this);

    if(this.tpl){
        if(typeof this.tpl == 'string'){
            this.tpl = new Ext.Template(this.tpl);
        }
        this.tpl.compile();
    }

    this.state = {};
    this.bodyContent = {};
};

Ext.extend(Ext.grid.RowExpander, Ext.util.Observable, {
    header: "",
    width: 20,
    sortable: false,
    fixed:true,
    menuDisabled:true,
    dataIndex: '',
    id: 'expander',
    lazyRender : true,
    enableCaching: true,

    getRowClass : function(record, rowIndex, p, ds){
        p.cols = p.cols-1;
        var content = this.bodyContent[record.id];
        if(!content && !this.lazyRender){
            content = this.getBodyContent(record, rowIndex);
        }
        if(content){
            p.body = content;
        }
        return this.state[record.id] ? 'x-grid3-row-expanded' : 'x-grid3-row-collapsed';
    },

    init : function(grid){
        this.grid = grid;

        var view = grid.getView();
        view.getRowClass = this.getRowClass.createDelegate(this);

        view.enableRowBody = true;

        grid.on('render', function(){
            view.mainBody.on('mousedown', this.onMouseDown, this);
        }, this);
    },

    getBodyContent : function(record, index){
        if(!this.enableCaching){
            return this.tpl.apply(record.data);
        }
        var content = this.bodyContent[record.id];
        if(!content){
            content = this.tpl.apply(record.data);
            this.bodyContent[record.id] = content;
        }
        return content;
    },

    onMouseDown : function(e, t){
        if(t.className == 'x-grid3-row-expander'){
            e.stopEvent();
            var row = e.getTarget('.x-grid3-row');
            this.toggleRow(row);
        }
    },

    renderer : function(v, p, record){
        p.cellAttr = 'rowspan="2"';
        return '<div class="x-grid3-row-expander">&#160;</div>';
    },

    beforeExpand : function(record, body, rowIndex){
        if(this.fireEvent('beforeexpand', this, record, body, rowIndex) !== false){
            if(this.tpl && this.lazyRender){
                body.innerHTML = this.getBodyContent(record, rowIndex);
            }
            return true;
        }else{
            return false;
        }
    },

    toggleRow : function(row){
        if(typeof row == 'number'){
            row = this.grid.view.getRow(row);
        }
        this[Ext.fly(row).hasClass('x-grid3-row-collapsed') ? 'expandRow' : 'collapseRow'](row);
    },

    expandRow : function(row){
        if(typeof row == 'number'){
            row = this.grid.view.getRow(row);
        }
        var record = this.grid.store.getAt(row.rowIndex);
        var body = Ext.DomQuery.selectNode('tr:nth(2) div.x-grid3-row-body', row);
        if(this.beforeExpand(record, body, row.rowIndex)){
            this.state[record.id] = true;
            Ext.fly(row).replaceClass('x-grid3-row-collapsed', 'x-grid3-row-expanded');
            this.fireEvent('expand', this, record, body, row.rowIndex);
        }
    },

    collapseRow : function(row){
        if(typeof row == 'number'){
            row = this.grid.view.getRow(row);
        }
        var record = this.grid.store.getAt(row.rowIndex);
        var body = Ext.fly(row).child('tr:nth(1) div.x-grid3-row-body', true);
        if(this.fireEvent('beforecollapse', this, record, body, row.rowIndex) !== false){
            this.state[record.id] = false;
            Ext.fly(row).replaceClass('x-grid3-row-expanded', 'x-grid3-row-collapsed');
            this.fireEvent('collapse', this, record, body, row.rowIndex);
        }
    }
});
/*
 * ExtJS Raptorizer Plugin 0.1
 * By Joshua M. Thompson
 *
 * Derived from:
 *
 * jQuery Raptorize Plugin 1.0
 * www.ZURB.com/playground
 * Copyright 2010, ZURB
 * Free to use under the MIT license.
 * http://www.opensource.org/licenses/mit-license.php
*/

/*jslint browser: true, devel: true, eqeqeq: false, onevar: false, plusplus: false, white: false */
/*global Ext: true */

Ext.namespace('Ext.ux');

Ext.ux.KeySequenceTrigger = Ext.extend(Ext.util.Observable, {
    sequence: "38,38,40,40,37,39,37,39,66,65",
    useAudio: false,
    single:   true,

    keys: [],

    constructor: function (config) {
        Ext.ux.KeySequenceTrigger.superclass.constructor.call(this, config);

        Ext.apply(this, config);

        this.addEvents('activate');

        if (!this.el) {
            this.el = Ext.getDoc();
        }

        this.el.on('keydown', this.onKeyDown, this);

        if (this.useAudio && (Ext.isGecko3 || Ext.isWebKit)) {
            var html = '<audio preload="auto">';

            for (var i = 0 ; i < this.audioUrls.length ; i++) {
                html = html + '<source src="' + this.audioUrls[i] + '" />';
            }

            html = html + '</audio>';

            this.audioEl = Ext.DomHelper.append(document.body, html, true);
        }
    },

    onKeyDown: function (e, t, o) {
        this.keys.push(e.getKey());

        if (this.keys.toString().indexOf(this.sequence) >= 0) {
            if (this.single) {
                this.el.un('keydown', this.onKeyDown, this);
            }

            if (this.audioEl) {
                this.audioEl.dom.play();
            }

            this.fireEvent('activate', this, this.el);
        }
    }
});

Ext.ux.Raptorizer = Ext.extend(Ext.util.Observable, {
    useAudio: true,     // wehther or not to play audio when the browser supports it

    imageUrl: 'raptor.png',
    audioUrls: ['raptor-sound.mp3', 'raptor-sound.ogg'],

    locked: false,

    constructor: function (config) {
        Ext.ux.Raptorizer.superclass.constructor.call(this, config);

        Ext.apply(this, config);

        this.createElements();

        if (this.triggerType == 'timer') {
            this.timeoutId = this.onActivate.defer(this.delayTime, this);
        }
        else if (this.triggerType == 'click') {
            this.el.on('click', this.onClick, this);
        }
        else if (this.triggerType == 'trigger'){
            if (!this.trigger) {
                this.trigger = new Ext.ux.KeySequenceTrigger(this.triggerConfig);
            }

            this.trigger.on('activate', this.onActivate, this);
        }
    },

    createElements: function () {
        this.imageEl = Ext.DomHelper.append(document.body, '<img style="display: none" src="' + this.imageUrl + '" />', true);
        this.imageEl.setStyle({
            "display": "block",
            "position": "fixed",
            "bottom": "-1000px",
            "right": "0"
        });

        if (this.useAudio && (Ext.isGecko3 || Ext.isWebKit)) {
            var html = '<audio preload="auto">';

            for (var i = 0 ; i < this.audioUrls.length ; i++) {
                html = html + '<source src="' + this.audioUrls[i] + '" />';
            }

            html = html + '</audio>';

            this.audioEl = Ext.DomHelper.append(document.body, html, true);
        }

    },

    onActivate: function () {
        this.locked = true;

        if (this.audioEl) {
            this.audioEl.dom.play();
        }

        this.startAnimation(this.imageEl);
    },

    onClick: function (e, t, o) {
        if (!this.locked && e.shiftKey) {
            e.stopEvent();

            this.onActivate();
        }
    },

    remove: function (el) {
        this.locked = true;

        if (this.triggerType == 'timer') {
            clearTimeout(this.timeoutId);
        }
        else if (this.triggerType == 'click') {
            this.el.un('click', this.onClick, this);
        }
        else if (this.triggerType == 'triggger') {
            this.trigger.un('activate', this.onActivate, this);
        }

        if (this.imageEl) {
            this.imageEl.remove();
        }

        if (this.audioEl) {
            this.audioEl.remove();
        }
    },

    startAnimation: function (el) {
        el.alignTo(document, 'b-b');
        el.sequenceFx();
        el.slideIn('b', { duration: 9 });
        el.puff({ duration: 6, remove: true });
    }
});

